Delphi FMX TImage 手动 alpha 透明度映射失败
Delphi FMX TImage manual alpha transparency mapping failure
我正在尝试使用代码创建具有 alpha 透明度的 TImage。
在这个例子中,一个消除锯齿的圆圈。
我为圆形创建了一个 8 位不透明度贴图,然后使用这段代码将它应用到 TImage 的 TBitmap 中:
type
TOpacityMap = Array[0..4095] of PByteArray;
procedure DrawAntiAliasedCircle(srcBitmap: TBitmap; FillColor : TAlphaColor; CenterX, CenterY, Radius, LineWidth, Feather: single);
var
FillR : Integer;
FillG : Integer;
FillB : Integer;
FillRGB : Integer;
OpacityMap : TOpacityMap;
AlphaScanLine : Array[0..4095] of TAlphaColor;
bitmapData : FMX.Graphics.TBitmapData;
tmpScanLine : Pointer;
X,Y : Integer;
tmpMS : TMemoryStream;
begin
{Initialization}
FillR := TAlphaColorRec(FillColor).R;
FillG := TAlphaColorRec(FillColor).G;
FillB := TAlphaColorRec(FillColor).B;
CreateAntiAliasedCircleOpacityMap(OpacityMap, srcBitmap.Width, srcBitmap.Height, CenterX, CenterY, Radius, LineWidth, Feather);
{create image based on opacity map and free memory}
If srcBitmap.Map(TMapAccess.Write, bitmapData) then
try
FillRGB := (FillR shl 16)+(FillG shl 8)+FillB;
for Y := 0 to srcBitmap.Height-1 do
begin
for X := 0 to srcBitmap.Width-1 do
AlphaScanLine[X] := (OpacityMap[Y][X] shl 24)+FillRGB; // Opacity
tmpScanLine := bitmapData.GetScanline(Y);
AlphaColorToScanLine(@AlphaScanLine,tmpScanLine,srcBitmap.Width,srcBitmap.PixelFormat);
FreeMem(OpacityMap[Y]);
end;
finally
srcBitmap.Unmap(bitmapData);
end;
// Work-around fix
{tmpMS := TMemoryStream.Create;
srcBitmap.SaveToStream(tmpMS);
srcBitmap.LoadFromStream(tmpMS);
tmpMS.Free;}
end;
结果是左边的图像。
实际的 TBitmap 似乎不错,调用 "srcBitmap.SaveToFile('circle.png')" 会生成具有有效 alpha 通道的 PNG 文件。
我可以通过简单地 saving/loading 使用 TMemoryStream 的位图来解决这个问题。
如何在不通过 TMemoryStream 传递图像的性能损失的情况下获得右侧的所需图像?
这些屏幕截图来自演示此问题的最小示例项目:
https://github.com/bLightZP/AntiAliasedCircle
编辑#1:
github 上面链接的代码已更新为 Tom Brunberg 建议修复的优化版本(大约快 25%)。
您可以通过对叠加图像应用 alpha 通道预乘来实现您的目标。例如,在您添加 alpha 通道的循环中:
for X := 0 to srcBitmap.Width-1 do
begin
AlphaScanLine[X] := (OpacityMap[Y][X] shl 24)+FillRGB;
AlphaScanLine[X] := PremultiplyAlpha(AlphaScanLine[X]); // Add this for premultiplied Alpha
end;
结果看起来像这样(当然没有流work-around)
我正在尝试使用代码创建具有 alpha 透明度的 TImage。 在这个例子中,一个消除锯齿的圆圈。
我为圆形创建了一个 8 位不透明度贴图,然后使用这段代码将它应用到 TImage 的 TBitmap 中:
type
TOpacityMap = Array[0..4095] of PByteArray;
procedure DrawAntiAliasedCircle(srcBitmap: TBitmap; FillColor : TAlphaColor; CenterX, CenterY, Radius, LineWidth, Feather: single);
var
FillR : Integer;
FillG : Integer;
FillB : Integer;
FillRGB : Integer;
OpacityMap : TOpacityMap;
AlphaScanLine : Array[0..4095] of TAlphaColor;
bitmapData : FMX.Graphics.TBitmapData;
tmpScanLine : Pointer;
X,Y : Integer;
tmpMS : TMemoryStream;
begin
{Initialization}
FillR := TAlphaColorRec(FillColor).R;
FillG := TAlphaColorRec(FillColor).G;
FillB := TAlphaColorRec(FillColor).B;
CreateAntiAliasedCircleOpacityMap(OpacityMap, srcBitmap.Width, srcBitmap.Height, CenterX, CenterY, Radius, LineWidth, Feather);
{create image based on opacity map and free memory}
If srcBitmap.Map(TMapAccess.Write, bitmapData) then
try
FillRGB := (FillR shl 16)+(FillG shl 8)+FillB;
for Y := 0 to srcBitmap.Height-1 do
begin
for X := 0 to srcBitmap.Width-1 do
AlphaScanLine[X] := (OpacityMap[Y][X] shl 24)+FillRGB; // Opacity
tmpScanLine := bitmapData.GetScanline(Y);
AlphaColorToScanLine(@AlphaScanLine,tmpScanLine,srcBitmap.Width,srcBitmap.PixelFormat);
FreeMem(OpacityMap[Y]);
end;
finally
srcBitmap.Unmap(bitmapData);
end;
// Work-around fix
{tmpMS := TMemoryStream.Create;
srcBitmap.SaveToStream(tmpMS);
srcBitmap.LoadFromStream(tmpMS);
tmpMS.Free;}
end;
结果是左边的图像。 实际的 TBitmap 似乎不错,调用 "srcBitmap.SaveToFile('circle.png')" 会生成具有有效 alpha 通道的 PNG 文件。
我可以通过简单地 saving/loading 使用 TMemoryStream 的位图来解决这个问题。
如何在不通过 TMemoryStream 传递图像的性能损失的情况下获得右侧的所需图像?
这些屏幕截图来自演示此问题的最小示例项目:
https://github.com/bLightZP/AntiAliasedCircle
编辑#1:
github 上面链接的代码已更新为 Tom Brunberg 建议修复的优化版本(大约快 25%)。
您可以通过对叠加图像应用 alpha 通道预乘来实现您的目标。例如,在您添加 alpha 通道的循环中:
for X := 0 to srcBitmap.Width-1 do
begin
AlphaScanLine[X] := (OpacityMap[Y][X] shl 24)+FillRGB;
AlphaScanLine[X] := PremultiplyAlpha(AlphaScanLine[X]); // Add this for premultiplied Alpha
end;
结果看起来像这样(当然没有流work-around)