2010-06-15 18 views
6

Escribí una pequeña clase de utilidad que guarda BitmapSource objetos en archivos de imagen. Los archivos de imagen pueden ser bmp, jpeg o png. Aquí está el código:¿Por qué obtengo resultados completamente diferentes al guardar un BitmapSource en bmp, jpeg y png en WPF

public class BitmapProcessor 
{ 
    public void SaveAsBmp(BitmapSource bitmapSource, string path) 
    { 
     Save(bitmapSource, path, new BmpBitmapEncoder()); 
    } 

    public void SaveAsJpg(BitmapSource bitmapSource, string path) 
    { 
     Save(bitmapSource, path, new JpegBitmapEncoder()); 
    } 

    public void SaveAsPng(BitmapSource bitmapSource, string path) 
    { 
     Save(bitmapSource, path, new PngBitmapEncoder()); 
    } 

    private void Save(BitmapSource bitmapSource, string path, BitmapEncoder encoder) 
    { 
     using (var stream = new FileStream(path, FileMode.Create)) 
     { 
      encoder.Frames.Add(BitmapFrame.Create(bitmapSource)); 
      encoder.Save(stream); 
     } 
    } 
} 

Cada uno de los tres métodos Save funciona, pero me da resultados inesperados con BMP y JPEG. Png es el único formato que produce una reproducción exacta de lo que veo si muestro el BitmapSource en la pantalla usando un control WPF Image.

Éstos son los resultados:


BMP-demasiado oscuro

too dark http://img822.imageshack.us/img822/7403/terrainbmp.png


JPEG-demasiado saturado

too saturated http://img816.imageshack.us/img816/8127/terrainjpeg.jpg


PNG - correctas

correct http://img810.imageshack.us/img810/6243/terrainpng.png


¿Por qué recibo resultados completamente diferentes para diferentes tipos de archivos?

Debo notar que el BitmapSource en mi ejemplo usa un valor alfa de 0.1 (por lo que parece muy desaturado), pero debería ser posible mostrar los colores resultantes en cualquier formato de imagen. Sé que si tomo una captura de pantalla usando algo como HyperSnap, se verá correctamente independientemente del tipo de archivo que guarde.


Aquí hay una captura de pantalla HyperSnap guardado como BMP:

correct http://img815.imageshack.us/img815/9966/terrainbmphypersnap.png

Como se puede ver, esto no es un problema, por lo que definitivamente hay algo extraño en codificadores de imagen de WPF.

¿Tengo una configuración incorrecta? ¿Me estoy perdiendo de algo?

+0

¿Está dibujando estas imágenes en la memoria y luego guardándolas? ¿O es de una imagen cargada en el disco? –

+0

@ Adam, estas imágenes se capturan desde una memoria (y en pantalla) 'Viewport3D' usando' RenderTargetBitmap.Render (UIElement) '. – devuxer

Respuesta

7

Personalmente, no creo que sea demasiado sorprendente ver lo que está viendo. BMP y JPG no admiten opacidad y PNG sí.

Tome este código, que crea un rectángulo azul parcialmente transparente en una imagen.

WriteableBitmap bm = new WriteableBitmap(100, 100, 96, 96, PixelFormats.Pbgra32, null); 
bm.Lock(); 

Bitmap bmp = new Bitmap(bm.PixelWidth, bm.PixelHeight, bm.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format32bppArgb, bm.BackBuffer); 
using(Graphics g = Graphics.FromImage(bmp)) { 
    var color = System.Drawing.Color.FromArgb(20, System.Drawing.Color.Blue); 
    g.FillRectangle(
     new System.Drawing.SolidBrush(color), 
     new RectangleF(0, 0, bmp.Width, bmp.Height)); 
} 

bmp.Save(@".\000_foo.bmp", System.Drawing.Imaging.ImageFormat.Bmp); 
bmp.Save(@".\000_foo.jpg", System.Drawing.Imaging.ImageFormat.Jpeg); 
bmp.Save(@".\000_foo.png", System.Drawing.Imaging.ImageFormat.Png); 

bmp.Dispose(); 

bm.AddDirtyRect(new Int32Rect(0, 0, bm.PixelWidth, bm.PixelHeight)); 
bm.Unlock(); 

new BitmapProcessor().SaveAsBmp(bm, @".\foo.bmp"); 
new BitmapProcessor().SaveAsJpg(bm, @".\foo.jpg"); 
new BitmapProcessor().SaveAsPng(bm, @".\foo.png"); 

Los formatos PNG siempre funcionan, ya sea System.Drawing o los codificadores WPF. Los codificadores JPG y BMP no funcionan. Muestran un rectángulo azul sólido.

La clave aquí es que no pude especificar un color de fondo en mi imagen. Sin un color de fondo, la imagen no se procesará correctamente en formatos que no admitan un canal alfa (BMP/JPG). Con una línea adicional de código:

g.Clear(System.Drawing.Color.White); 
g.FillRectangle(
    new System.Drawing.SolidBrush(color), 
    new RectangleF(0, 0, bmp.Width, bmp.Height)); 

Mi imagen tiene un color de fondo, por lo que los codificadores que no soportan un canal alfa puede determinar cuál es el color de salida debe ser por píxel. Ahora todas mis imágenes se ven correctas.

En su caso, debe RenderTargetBitmap un control con un color de fondo especificado o pintar un color de fondo cuando está renderizando su imagen.

Y digo, el motivo por el que funciona su pantalla de impresión de terceros es que, en última instancia, los colores transparentes tienen un color de fondo en ese punto (estando en una ventana que tiene un color de fondo). Pero dentro de WPF, se trata de elementos que no tienen un conjunto; el uso de RTB en un elemento no hereda sus propiedades de varios elementos principales, como el color de fondo.

+0

+1. Gracias, Adam! Tiene mucho sentido que no tener antecedentes trastorna las cosas. Curiosamente, si nos fijamos en el bmp salvado de HyperSnap, tiene un fondo azul muy claro. Con el png, es completamente transparente. – devuxer

+0

¿Es un fondo azul claro o un fondo azul parcialmente transparente? ¿Y estás seguro de que el fondo está en el elemento (o niños) que estás RTB? Si es un nivel más alto, no cuenta. –

Cuestiones relacionadas