2011-11-21 38 views
5

我正在使用GR32繪製多個半透明PNG圖像。 到目前爲止,我一直在使用下面的方法:Delphi,GR32 + PngObject:轉換爲Bitmap32不能正常工作

png:= TPNGObject.Create; 
    png.LoadFromFile(...); 
    PaintBox321.Buffer.Canvas.Draw(120, 20, png); 

但我想改用提出了GR32網站的方法(http://graphics32.org/wiki/FAQ/ImageFormatRelated):

tmp:= TBitmap32.Create; 
    LoadPNGintoBitmap32(tmp, ..., foo); 
    tmp.DrawMode:= dmBlend; 
    PaintBox321.Buffer.Draw(Rect(20, 20, 20+ tmp.Width, 20+tmp.Height), 
    tmp.ClipRect, tmp); 

雖然第一種方法工作完全正常,第二個 - 應該給出相同的結果 - 導致Alpha通道非常奇怪的問題,請參閱圖像(它也顯示與Paint.NET中「排列」的相同圖像的比較 - 背景和圖標均在編輯器的圖層上打開)。該圖像描述了Bitmap32的加載或繪製不當。有小費嗎?

Problem with TBitmap32 alpha channel

- 加11月22日

我發現它是不是畫,它是關於加載PNG到BMP32。從BMP32保存到PNG會生成不正確的「白化」(左邊的那個)PNG圖像。

+1

我認爲這是由於「不透明度」,還你設置「tmp.DrawMode:= dmBlend;」我沒有使用GR32,但我猜想,所不同的是,由於不透明性。 – ComputerSaysNo

+1

@Dinin Duminica,它不是。他們網站上的例子顯示,如果加載的PNG圖像中有任何透明度,模式應該是dmBlend。因爲我知道我所有的圖片都是透明的,所以我不必檢查。 – migajek

回答

9

原因似乎是,當透明圖像被加載到LoadPNGintoBitmap32時,圖像被應用了兩次,使圖像看起來更加透明和灰色(稍後會詳細討論)。

首先透明度:

這是從原來的LoadPNGintoBitmap32代碼,關鍵部位均標有註釋:

PNGObject := TPngObject.Create; 
PNGObject.LoadFromStream(srcStream); 

destBitmap.Assign(PNGObject); // <--- paint to destBitmap's canvas with transparency (!) 
destBitmap.ResetAlpha;   

case PNGObject.TransparencyMode of // <--- the following code sets the transparency again for the TBitmap32 
{ ... } 

destBitmap.Assign內部不一樣的,你在你以前的方法:它讓我們PNG圖像將其自身繪製到其畫布上。該操作遵循PNG的Alpha通道。但這不是必須的,因爲alpha通道在第二步中被分配到TBitmap32的像素!

現在改變代碼如下,關鍵部分再次標有註釋:

PNGObject := TPngObject.Create; 
PNGObject.LoadFromStream(srcStream); 

PNGObject.RemoveTransparency; // <--- paint PNG without any transparency... 
destBitmap.Assign(PNGObject); // <--- ...here 
destBitmap.ResetAlpha; 

srcStream.Position:=0; 
PNGObject.LoadFromStream(srcStream); // <--- read the image again to get the alpha channel back 

case PNGObject.TransparencyMode of // <--- this is ok now, the alpha channel now only exists in the TBitmap32 
{ ... } 

上述溶液是低效的,因爲它讀取圖像的兩倍。但它顯示了爲什麼你的第二種方法產生更透明的圖像。

對於灰色:原始代碼還有一個問題:destBitmap.Assign先用clWhite32填充背景,然後將圖像透明地繪製到背景上。然後LoadPNGintoBitmap32來了,並在其上增加了另一層透明度。

+0

謝謝你,這應該是一個完美的答案時尚 - 不僅是更正的代碼,但也是非常詳細的解釋,什麼是錯的:) – migajek

+1

呵呵,不客氣:)這是一個有趣的和很好的問題! –

0

問題可能是PNG錯誤地轉換爲TBitmap32,丟失了傳輸中的透明度信息。這是調色板PNG圖像的常見情況。否則,您不必使用「Bitmap.DrawMode:= dmTransparent」和「OuterColor」。如果來自PNG的透明信息將正確傳輸到TBitmpa32,則DrawMode:= dmBlend可以工作,而不需要設置OuterColor。

最重要的是您如何將PNG加載到TBitmap32中。來自Vcl.Imaging的TPngImage。pngimage單元(在Delphi XE2和更高版本中實現)可以在位圖上透明地繪製,保留位圖上的內容,使用PNG alpha圖層組合顏色等,但不能輕鬆轉換各種PNG透明度格式(包括調色板)轉換爲TBitmap32每個像素的alpha分量。一旦TPngImage繪製圖像,您將得到每個像素的組合RGB,但alpha分量不會傳輸到目標位圖。

有跡象表明,嘗試將PNG加載與透明度TBitmap32提供幫助程序,但他們也有缺點:

(1)從http://graphics32.org/wiki/FAQ/ImageFormatRelated 「LoadPNGintoBitmap32」 - 它適用透明度的兩倍,因此與圖像除0或255以外的Alpha值與其他軟件(在玻璃效果的半透明圖像上最明顯)看起來不同。此代碼首先將alpha應用於RGB,然後將alpha設置爲單獨的圖層,因此當繪製時,alpha將再次應用。你可以在這裏找到關於這個問題的更多信息:Delphi, GR32 + PngObject: converting to Bitmap32 doesn't work as expected 。除此之外,它不會正確地將調色板圖像的透明度轉換爲TBitmap32的alpha層。他們手動爲輸出位圖(渲染爲RGB)的某個顏色的像素手動設置alpha透明度,而不是在渲染爲RGB之前執行此操作,因此當所有白色像素都透明時,實際透明度會丟失,因爲您的示例圖像會丟失實際透明度。 (2)來自gr32ex庫的「LoadBitmap32FromPNG」:https://code.google.com/archive/p/gr32ex/ - 與(1)相同的算法的稍微不同的實現,並且與(1)具有相同的問題。

因此,解決方案是:

  1. 不要使用TBitmap32;使用Vcl.Imaging.pngimage.TPngImage直接在目標位圖(屏幕等)上繪製 - 這是與各種PNG格式正確處理的最兼容的方式。
  2. 使用助手路由將透明度信息從Vcl.Imaging.pngimage.TPngImage傳輸到TBitmap32。
  3. 使用GR32 PNG庫,可以將PNG本地加載到TBitmap32中https://sourceforge.net/projects/gr32pnglibrary/ 由於您現在擁有關於此問題的所有信息,因此您可能會爲您找到合適的解決方案。

如何加載阿爾法層在一次通過

海因裏希烏布利希了一個漂亮的建議paining之前除去透明層,然後再次讀取的圖像。爲避免加載圖像兩次,您可以在調用PNGObject.RemoveTransparency之前保存alpha圖層。以下是正確應用alpha圖層並僅加載一次圖像的代碼。不幸的是,它不適用於調色板圖像。如果您知道如何從任何調色板圖像正確填充TBitmap32的alpha圖層,但沒有在Transparent Png to TBitmap32中描述的效果,請讓我知道。

procedure LoadPNGintoBitmap32(DstBitmap: TBitmap32; SrcStream: TStream; out AlphaChannelUsed: Boolean); 
var 
    PNGObject: TPngImage; 
    PixelPtr: PColor32; 
    AlphaPtr: PByte; 
    SaveAlpha: PByte; 
    I, AlphaSize: Integer; 
begin 
    AlphaChannelUsed := False; 
    PNGObject := TPngImage.Create; 
    try 
    PNGObject.LoadFromStream(SrcStream); 
    AlphaPtr := PByte(PNGObject.AlphaScanline[0]); 
    if Assigned(AlphaPtr) then 
    begin 
     AlphaSize := PNGObject.Width * PNGObject.Height; 
     if AlphaSize <= 0 then raise Exception.Create('PNG files with zero dimensions are not supported to be loaded to TBitmap32'); 
     GetMem(SaveAlpha, AlphaSize); 
     try 
     Move(AlphaPtr^, SaveAlpha^, AlphaSize); 
     PNGObject.RemoveTransparency; 
     DstBitmap.Assign(PNGObject); 
     DstBitmap.ResetAlpha; 
     PixelPtr := PColor32(@DstBitmap.Bits[0]); 
     AlphaPtr := SaveAlpha; 
     for I := 0 to AlphaSize-1 do 
     begin 
      PixelPtr^ := (PixelPtr^ and $00FFFFFF) or (TColor32(AlphaPtr^) shl 24); 
      Inc(PixelPtr); 
      Inc(AlphaPtr); 
     end; 
     finally 
     FreeMem(SaveAlpha, AlphaSize); 
     end; 
     AlphaChannelUsed := True; 
    end else 
    if PNGObject.TransparencyMode = ptmNone then 
    begin 
     DstBitmap.Assign(PNGObject); 
    end else 
    begin 
     raise Exception.Create('Paletted PNG images are not supported in LoadPNGintoBitmap32, transparency cannot be stored to TBitmap32'); 
    end; 
    finally 
    FreeAndNil(PNGObject); 
    end; 
end;