Почему я получаю совершенно разные результаты при сохранении BitmapSource в bmp, jpeg и png в WPF - PullRequest
6 голосов
/ 15 июня 2010

Я написал небольшой служебный класс, который сохраняет 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.Png - единственный формат, который дает точное воспроизведение того, что я вижу, если я показываю BitmapSource на экране, используя элемент управления WPF Image.

Вот результаты:


BMP - слишком темно

слишком темно http://img822.imageshack.us/img822/7403/terrainbmp.png


JPEG - слишком насыщенно

слишком насыщенно http://img816.imageshack.us/img816/8127/terrainjpeg.jpg


PNG - правильно

правильно http://img810.imageshack.us/img810/6243/terrainpng.png


Почему я получаю совершенно разные результаты для разных типов файлов?

Следует отметить, что BitmapSource в моем примере использует альфа-значение 0,1 (именно поэтому оно выглядит очень ненасыщенным), но должна быть возможность показать результирующие цвета в любом формате изображения.Я знаю, что если я сделаю снимок экрана с использованием чего-то вроде HyperSnap, он будет выглядеть правильно независимо от того, к какому типу файлов я сохраняю.


Вот снимок экрана HyperSnap, сохраненный как bmp:

правильно http://img815.imageshack.us/img815/9966/terrainbmphypersnap.png

Как видите, это не проблема, поэтому определенно есть что-то странное в кодировщиках изображений WPF.

У меня неправильная настройка?Я что-то упустил?

1 Ответ

7 голосов
/ 15 июня 2010

Лично я не думаю, что слишком удивительно видеть то, что вы видите.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 не работают.Они показывают сплошной синий прямоугольник.

Ключ в том, что я не смог указать цвет фона в моем изображении.Без цвета фона изображение не будет правильно отображаться в форматах, которые не поддерживают альфа-канал (BMP / JPG).С одной дополнительной строкой кода:

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

Мое изображение имеет цвет фона, поэтому кодировщики, которые не поддерживают альфа-канал, могут определить, какой должен быть выходной цвет на пиксель.Теперь все мои изображения выглядят корректно.

В вашем случае вы должны либо RenderTargetBitmap элемент управления с указанным цветом фона, либо закрасить фоновый цвет при рендеринге вашего изображения.причина, по которой ваш сторонний экран печати работает, заключается в том, что в конечном итоге прозрачные цвета имеют фоновый цвет в этой точке (находясь в окне с фоновым цветом).Но внутри WPF вы имеете дело с элементами, у которых нет одного набора;использование RTB для элемента не наследует его различные свойства родительского элемента, такие как цвет фона.

...