2010-06-15 16 views
6

Napisałem małą klasę narzędzi, która zapisuje obiekty BitmapSource do plików graficznych. Pliki obrazów mogą być bmp, jpeg lub png. Oto kod:Dlaczego uzyskuję zupełnie różne wyniki podczas zapisywania BitmapSource do bmp, jpeg i png w 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); 
     } 
    } 
} 

Każdy z trzech Save metod pracy, ale otrzymuję nieoczekiwane wyniki z BMP i JPEG. Png jest jedynym formatem, który zapewnia dokładną reprodukcję tego, co widzę, jeśli pokażę ekran BitmapSource przy użyciu kontrolki WPF Image.

tutaj wyniki:


BMP - zbyt ciemny

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


JPEG - zbyt nasyconego

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


PNG - poprawne

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


Dlaczego otrzymuję zupełnie różne wyniki dla różnych typów plików?

Należy zauważyć, że BitmapSource w moim przykładzie używa wartości alpha 0,1 (dlatego wydaje się bardzo desaturated), ale powinno być możliwe, aby pokazać uzyskane kolory w dowolnym formacie obrazu. Wiem, że jeśli zrobię zrzut ekranu za pomocą czegoś takiego jak HyperSnap, będzie wyglądać poprawnie, bez względu na to, jaki typ pliku zapisuję.


Oto zrzut ekranu HyperSnap zapisany jako BMP:

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

Jak widać, nie jest to problem, więc na pewno coś dziwnego przetworników obrazu WPF użytkownika.

Czy ustawienie jest nieprawidłowe? Czy czegoś brakuje?

+0

Czy rysujesz te obrazy w pamięci, a następnie je zapisujesz? A może z obrazu ładowanego na dysk? –

+0

@Adam, obrazy te są przechwytywane z "Viewport3D" w pamięci (i na ekranie) za pomocą 'RenderTargetBitmap.Render (UIElement)'. – devuxer

Odpowiedz

7

Nie uważam za zbyt zaskakujące widzieć to, co widzisz. BMP i JPG nie obsługują krycia, a PNG to robi.

Wykonaj ten kod, który tworzy częściowo przezroczysty niebieski prostokąt na obrazku.

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

Formaty PNG zawsze działają, niezależnie od tego, czy jest to System.Drawing, czy kodery WPF. Enkodery JPG i BMP nie działają. Pokazują solidny niebieski prostokąt.

Kluczem jest to, że nie udało mi się określić koloru tła na moim obrazku. Bez koloru tła obraz nie będzie renderowany poprawnie w formatach, które nie obsługują kanału alfa (BMP/JPG). Z jednej dodatkowej linii kodu:

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

Mój obraz ma kolor tła, więc kodery, które nie obsługują kanał alfa może określić, jakie kolory powinny być wyjście na piksel. Teraz wszystkie moje zdjęcia wyglądają poprawnie.

W twoim przypadku powinieneś albo RenderowaćTrzymajBitmapy kontrolki z określonym kolorem tła, albo pomaluj kolor tła podczas renderowania obrazu.

A FYI, powód, dla którego działa ekran drukowania trzeciej strony, polega na tym, że ostatecznie przezroczyste kolory mają kolor tła w tym miejscu (będąc w oknie, które ma kolor tła). Ale wewnątrz WPF masz do czynienia z elementami, które nie mają jednego zestawu; użycie RTB na elemencie nie dziedziczy jego różnych właściwości elementu rodzica, takich jak kolor tła.

+0

+1. Dzięki, Adam! To ma wiele sensu, że nie ma tła, które burzy wszystko. Co ciekawe, jeśli spojrzysz na bmp z HyperSnap, ma bardzo jasnoniebieskie tło. Z png jest całkowicie przezroczysty. – devuxer

+0

Czy to jasnoniebieskie tło lub częściowo przezroczyste niebieskie tło? I czy jesteś pewien, że tło znajduje się na elemencie (lub dzieciach), w którym RTB'ujesz? Jeśli jest wyższy o jeden poziom, to się nie liczy. –

Powiązane problemy