问题可能在于PNG图像被错误地转换为TBitmap32,在传输过程中丢失了透明度信息。这是调色板PNG图像的常见情况。否则,您不需要使用“Bitmap.DrawMode:= dmTransparent”和“OuterColor”。如果来自PNG的透明信息能够正确传输到TBitmpa32,则DrawMode:= dmBlend将起作用,而无需设置OuterColor。
最重要的是,您如何将PNG加载到TBitmap32中。来自Vcl.Imaging.pngimage单元(在Delphi XE2及更高版本中实现)的TPngImage可以在位图上透明地绘制,保留了该位图上的内容,使用PNG alpha层组合颜色等,但它不允许轻松地将各种格式的PNG透明度(包括调色板)转换为每个像素的alpha分量TBitmap32。一旦TPngImage绘制了图像,您就会得到每个像素的组合RGB,但是alpha分量不会传输到目标位图。
有可用的辅助程序例程尝试以透明方式将PNG加载到TBitmap32中,但它们具有缺点:
(1) “LoadPNGintoBitmap32” 来自 http://graphics32.org/wiki/FAQ/ImageFormatRelated
- 它会将透明度应用两次,因此具有介于0或255之间的Alpha值的图像与其他软件中的外观不同(在具有玻璃效果的半透明图像上最为明显)。该代码将首先将Alpha应用于RGB,然后将Alpha设置为单独的图层,因此当您绘制时,Alpha将再次应用。您可以在此处找到有关此问题的更多信息:Delphi, GR32 + PngObject:转换为Bitmap32无法按预期工作
。除此之外,它无法正确地将调色板图像的透明度转换为TBitmap32的Alpha层。他们手动为输出位图(渲染为RGB)的特定颜色的像素设置Alpha透明度,而不是在渲染为RGB之前这样做,因此实际的透明度会丢失,就像您的示例图像中所有白色像素都是透明的一样。
(2) gr32ex库中的“LoadBitmap32FromPNG”:https://code.google.com/archive/p/gr32ex/
- 这是与(1)略有不同的算法实现,并且存在与(1)相同的问题。
因此,解决方案如下:
- 不要使用TBitmap32;使用Vcl.Imaging.pngimage.TPngImage直接在目标位图(屏幕等)上进行绘制 - 这是最兼容的方式,可以正确处理各种PNG格式。
- 使用帮助程序路由从Vcl.Imaging.pngimage.TPngImage传输透明度信息到TBitmap32。
- 使用GR32 PNG库,它可以本地加载PNG到TBitmap32 https://sourceforge.net/projects/gr32pnglibrary/
既然您现在已经获得了有关此问题的所有信息,您可以为自己找到正确的解决方案。
如何一次加载alpha层
Heinrich Ulbricht提出了一个好建议,在绘制之前移除透明层,然后再读取图像。为了避免两次加载图像,您可以在调用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;