2010-05-10 14 views
12

¿Cómo puedo guardar una imagen con su codificación original?¿Cómo guardar una imagen en su formato original?

Parece que la única forma de guardar una imagen es utilizando un BitmapEncoder, pero no sé cómo puedo obtener el formato correcto de la imagen.

Ejemplo:

Clipboard.GetImage() devuelve un InteropBitmap que no parece contener ninguna información sobre el formato original.

También probé utilizando un método de extensión:

public static void Save(this BitmapImage image, System.IO.Stream stream) 
{ 
    var decoder = BitmapDecoder.Create(image.StreamSource, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); 
    var encoder = BitmapEncoder.Create(decoder.CodecInfo.ContainerFormat); 
    foreach (var frame in decoder.Frames) 
    { 
     encoder.Frames.Add(BitmapFrame.Create(frame)); 
    } 
    encoder.Save(stream); 
} 

pero el problema es que

  1. la ImageSource no siempre es una BitmapImage (Clipboard.GetImage()) por ejemplo y
  2. la imagen.StreamSource puede ser nula en algunos casos (parece ser cuando la imagen se carga a través de un Uri relativo)

¿Alguna sugerencia?

+0

¿Puede explicar el contexto en el que está experimentando esto? Me parece que si está guardando una imagen, simplemente guárdela en el formato que desee. El BitmapImage parece ser un formato, y la imagen original ya se ha convertido. –

Respuesta

19

La pregunta fundamental aquí es "¿Cómo tomo una ImageSource arbitraria y aprendo en qué formato se codificó originalmente?"

La respuesta es que no siempre se puede hacer esto, pero para la mayoría de los fines prácticos hay una solución disponible. Como muestra el código anterior, una vez que haya aprendido qué formato usar, el resto es fácil.

La razón por la que no siempre se puede descubrir el formato original es porque es posible (con un código muy complicado) subcategorizar BitmapSource para crear un nuevo ImageSource que obtiene sus datos desde cualquier lugar que desee. Por ejemplo, podría implementar un PseudoRandomBitmapSource que devuelva píxeles aleatorios. En este caso, el "formato original" es probablemente la semilla utilizada para el generador de números aleatorios.

Si se trata de uno de los incorporados en las clases ImageSource, la forma de averiguar la codificación original depende de qué clase exacto que esté utilizando:

  1. Para BitmapImage, puede utilizar StreamSource o UriSource , cualquiera que esté establecido. Cualquiera de estos se puede pasar a BitmapDecoder.Crear sobrecarga. Su código de ejemplo muestra cómo hacer esto para StreamSource. Es exactamente lo mismo para UriSource, excepto que necesita el nuevo Uri(BaseUri, UriSource) y pasarlo al BitmapDecoder.Create overload that takes a Uri.

  2. Para ColorConvertedBitmap, CroppedBitmap, FormatConvertedBitmap, y TransformedBitmap existe una propiedad de "Fuente" pública que puede usar para obtener la fuente subyacente, luego verifica recursivamente su codificación utilizando este algoritmo.

  3. En CachedBitmap, solo puede acceder al mapa de bits de origen a través de un campo interno. Puede acceder utilizando el reflejo si tiene suficientes privilegios, de lo contrario no tendrá suerte.

  4. Para RenderTargetBitmap, WritableBitmap, D3DImage y DrawingImage no existe una codificación original, ya que la imagen se está construyendo "sobre la marcha" a partir de un formato vectorial o un algoritmo.

  5. Para BitmapFrame, use la propiedad Decoder para obtener el decodificador, luego use CodecInfo.ContainerFormat.

  6. Para Interopitmap o UnmanagedBitmapWrapper es muy complicado. Básicamente, debe usar el reflejo para leer la propiedad WicSourceHandle interna, luego llame a DangerousGetHandle() para obtener un IntPtr que en realidad es un IUnkown no administrado. Usando código no administrado, QueryInterface para IWICBitmapDecoder. Si tiene éxito, puede llamar a IWICBitmapDecoder.GetContainerFormat para obtener el formato Guid (este sigue siendo un código no administrado). De lo contrario, toda la información sobre la fuente original se ha perdido.

Como se puede ver, hay un buen número de situaciones en las que no se puede conseguir la fuente (por ejemplo, un InteropBitmap de un mapa de bits totalmente decodificado) o donde conseguir la fuente requiere técnicas especiales y privilegios (código no administrado de Interopitmap, o reflexión en campos internos para CachedBitmap).

Dado que a menudo es difícil o imposible obtener el formato original, es una buena idea buscar formas de preservar esta información a medida que se transmite a su código. Si el control de la fuente de la imagen, puede ser tan simple como crear una clase ImageSourceAndFormat:

public class BitmapSourceAndFormat 
{ 
    public ImageSource Source { get; set; } 
    public Guid OriginalFormat { get; set; } 
} 

Esto es particularmente útil si se va a poner la imagen en el portapapeles. Además de agregar la imagen al objeto DataObject normalmente, también puede agregar un objeto BitmapSourceAndFormat. Si hace esto, la operación Pegar recibirá un DataObject que contiene ambos formatos. Su código solo necesita verificar primero el objeto BitmapSourceAndFormat. Si se encuentra, simplemente puede acceder a él para obtener el formato original. Si no, debe recurrir a los medios descritos anteriormente.

Una última nota: para las pastas del portapapeles, puede verificar las cadenas de formato de datos disponibles. Algunos útiles son: Bitmap, Dib, Tiff, Gif, Jpeg y FileName. Para "Tiff", "Gif" y "Jpeg", puede codificar el formato requerido. Para "Nombre de archivo", puede abrir el archivo usted mismo para que una transmisión pase a BitmapDecoder. Para "Dib" o "Bitmap" sin otra cosa, usted sabe que el formato original se ha perdido.

+0

guau - esta es una excelente respuesta. Muchas gracias! ¡Excelentes consejos sobre copiar/pegar! –

+0

+ gracias por no solo decirme lo que quería saber, sino también decirme cuál es mi pregunta fundamental. Debería haberlo descrito mejor. –

Cuestiones relacionadas