问题可能是 PNG 错误地转换为 TBitmap32,在传输过程中丢失了透明度信息。这是调色板PNG图像的常见情况。否则,您不必使用“Bitmap.DrawMode := dmTransparent”和“OuterColor”。如果来自 PNG 的透明信息可以正确传输到 TBitmpa32,DrawMode := dmBlend 就可以工作,而无需设置 OuterColor。
最重要的是如何将 PNG 加载到 TBitmap32 中。Vcl.Imaging.pngimage 单元中的 TPngImage(在 Delphi XE2 及更高版本中实现)可以在位图上透明绘制,保留位图上的内容,使用 PNG alpha 层组合颜色等,但它不允许轻松转换各种将 PNG 透明度(包括调色板)格式转换为 TBitmap32 的每个像素的 alpha 分量。一旦 TPngImage 绘制了图像,您将获得每个像素的组合 RGB,但 alpha 分量不会传输到目标位图。
有一些辅助例程可以尝试将 PNG 加载到具有透明度的 TBitmap32 中,但它们有缺点:
(1) 来自http://graphics32.org/wiki/FAQ/ImageFormatRelated的“LoadPNGintoBitmap32”
- 它应用了两次透明度,因此 alpha 值不是 0 或 255 的图像看起来与其他软件中的不同(半透明时最明显具有玻璃效果的图像)。此代码将首先将 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。
- 使用可以将 PNG 本地加载到 TBitmap32 的 GR32 PNG 库https://sourceforge.net/projects/gr32pnglibrary/
由于您现在拥有有关此问题的所有信息,因此您可能会获得适合您的解决方案。
如何一次性加载 Alpha 层
Heinrich Ulbricht 提出了一个很好的建议,即在绘画之前移除透明层,然后再次读取图像。为避免两次加载图像,您可以在调用 PNGObject.RemoveTransparency 之前保存 Alpha 层。这是正确应用 alpha 层并仅加载图像一次的代码。不幸的是,它不适用于调色板图像。如果您知道如何从任何调色板图像中正确填充 TBitmap32 的 alpha 层,但没有透明 Png 到 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;