在WPF中将BitmapSource保存到bmp,jpeg和png时,为什么会得到完全不同的结果

时间:2010-06-15 00:54:59

标签: c# wpf image image-processing

我写了一个小实用程序类,它将BitmapSource个对象保存到图像文件中。图像文件可以是bmp,jpeg或png。这是代码:

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);
        }
    }
}

三种Save方法中的每一种都有效,但是我用bmp和jpeg得到了意想不到的结果。如果我使用WPF BitmapSource控件在屏幕上显示Image,则Png是唯一能够精确再现我看到的格式。

结果如下:


BMP - 太黑

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


JPEG - 过于饱和

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


PNG - 正确

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


为什么我会针对不同的文件类型获得完全不同的结果?

我应该注意到,我的示例中的BitmapSource使用了0.1的alpha值(这就是为什么它看起来非常饱和),但是应该可以以任何图像格式显示结果颜色。我知道如果我使用HyperSnap之类的东西进行屏幕截图,无论我保存什么文件类型,它都会看起来正确。


这是一个保存为bmp的HyperSnap屏幕截图:

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

正如您所看到的,这不是问题,因此WPF的图像编码器肯定有些奇怪。

我有设置错误吗?我错过了什么吗?

1 个答案:

答案 0 :(得分:7)

我个人认为看到你所看到的东西并不令人惊讶。 BMP和JPG不支持不透明度和PNG。

使用此代码,在图像中创建部分透明的蓝色矩形。

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" );

PNG格式始终有效,无论是System.Drawing还是WPF编码器。 JPG和BMP编码器不起作用。它们显示一个坚实的蓝色矩形。

这里的关键是我没有在图像中指定背景颜色。如果没有背景颜色,图像将无法以不支持Alpha通道(BMP / JPG)的格式正确呈现。增加一行代码:

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

我的图像具有背景颜色,因此不支持Alpha通道的编码器可以确定每个像素的输出颜色应该是多少。现在我的所有图像看起来都是正确的。

在您的情况下,您应该使用指定背景颜色的RenderTargetBitmap控件,或者在渲染图像时绘制背景颜色。

FYI,你的第三方打印屏幕工作的原因是最终透明颜色在该点具有背景颜色(在具有背景颜色的窗口上)。但是在WPF中,你处理的是没有一个元素的元素;在元素上使用RTB不会继承其各种父元素的属性,如背景颜色。