如何正确使用TBitmap对象来保存透明文件?
How to use correctly TBitmap object to save a file with transparency?
下面是我的示例代码:
var lBitmap: TBitmap;
begin
lBitmap := TBitmap.Create;
lBitmap.PixelFormat := TPixelFormat.pf32bit;
lBitmap.Transparent := TRUE; // !
lBitmap.LoadFromFile( 'd:\temp\bmp32b_300dpi_transparent_400x250.bmp' );
// Bitmap RGB+Alpha created with GIMP
// modifications on pixels
Canvas.Draw(100, 0, lBitmap);
// Up to this point it is correct, the drawing is painted with transparency
lBitmap.SaveToFile( 'd:\tmp\after.bmp' ); // after this -> I have lost transparency
lBitmap.Free;
end;
如何正确使用TBitmap对象保存透明文件?
在我看来 TBitmap
不支持保存带有 alpha 通道的位图。也许我们不应该为此责怪 VCL,因为具有 alpha 透明度的 BMP 并不常见。许多应用程序不支持透明 BMP。
话虽如此,我“reverse-engineered”了一个在 GIMP 中创建的带 alpha 通道的 BMP,并编写了以下 Delphi 例程来生成完全相同的位图:
procedure SaveTransparentBitmap(ABitmap: TBitmap; const AFileName: string);
var
FS: TFileStream;
BFH: TBitmapFileHeader;
BIH: TBitmapV5Header;
y: Integer;
sl: PUInt64;
begin
// ABitmap MUST have the GIMP BGRA format.
FS := TFileStream.Create(AFileName, fmOpenWrite);
try
// Bitmap file header
FillChar(BFH, SizeOf(BFH), 0);
BFH.bfType := D42; // BM
BFH.bfSize := 4 * ABitmap.Width * ABitmap.Height + SizeOf(BFH) + SizeOf(BIH);
BFH.bfOffBits := SizeOf(BFH) + SizeOf(BIH);
FS.Write(BFH, SizeOf(BFH));
// Bitmap info header
FillChar(BIH, SizeOf(BIH), 0);
BIH.bV5Size := SizeOf(BIH);
BIH.bV5Width := ABitmap.Width;
BIH.bV5Height := ABitmap.Height;
BIH.bV5Planes := 1;
BIH.bV5BitCount := 32;
BIH.bV5Compression := BI_BITFIELDS;
BIH.bV5SizeImage := 4 * ABitmap.Width * ABitmap.Height;
BIH.bV5XPelsPerMeter := 11811;
BIH.bV5YPelsPerMeter := 11811;
BIH.bV5ClrUsed := 0;
BIH.bV5ClrImportant := 0;
BIH.bV5RedMask := [=10=]FF0000;
BIH.bV5GreenMask := [=10=]00FF00;
BIH.bV5BlueMask := [=10=]0000FF;
BIH.bV5AlphaMask := $FF000000;
BIH.bV5CSType := 524742; // BGRs
BIH.bV5Intent := LCS_GM_GRAPHICS;
FS.Write(BIH, SizeOf(BIH));
// Pixels
for y := ABitmap.Height - 1 downto 0 do
begin
sl := ABitmap.ScanLine[y];
FS.Write(sl^, 4 * ABitmap.Width);
end;
finally
FS.Free;
end;
end;
这里写一个BITMAPFILEHEADER
followed by a BITMAPV5HEADER
和BGRA格式的像素数据。
我省略了各种错误检查。例如,我不验证 ABitmap
是否确实具有所需的格式。
测试:
procedure TForm1.FormCreate(Sender: TObject);
var
bm: TBitmap;
begin
bm := TBitmap.Create;
try
bm.LoadFromFile('C:\Users\Andreas Rejbrand\Desktop\Test.bmp');
SaveTransparentBitmap(bm, 'C:\Users\Andreas Rejbrand\Desktop\Test2.bmp');
finally
bm.Free;
end;
end;
在此之后,Test.bmp
和Test2.bmp
二进制相等。
正如@Andreas Rejbrand 指出的那样,使用 alpha 通道保存 32 位位图需要一个解决方法。关于 BMP 文件格式、TBitmap.Transparent
属性 的作用以及如何使用 VCL 透明地绘制位图,似乎还有一些混淆。
32 位位图是唯一携带文件透明度信息的位图。他们在 alpha 通道中拥有该信息,而在其他任何地方都没有。在 alpha 通道中,每个像素在 RGBA 结构中都有自己的 0-255 alpha 值。这通常被称为部分透明。
当你draw/display32位位图时,你要注意TBitmap.AlphaFormat
属性。它默认为 afIgnore
,这意味着位图是在不透明的情况下绘制的。使用 afPremultiplied
或 afDefined
绘制透明。后者可能就是你想要的。
TBitmap.Transparent
属性是VCL特有的TBitmap
,BMP文件格式中没有与之对应的东西。这只是一种透明显示位图的简单方法,其中一种颜色定义了哪些像素应该完全透明。应用程序必须熟悉位图才能使用此方法。了解 TBitmap.TransparentMode
属性 的工作原理也很重要。它默认为 tmAuto
,它将位图的 bottom-leftmost 像素的颜色设置为 TBitmap.TransparentColor
。当 TransparentMode
设置为 tmFixed
时,将使用您指定的 TBitmap.TransparentColor
。此方法也可用于 32 位位图。
请注意,当您在 32 位位图上使用标准 VCL TCanvas
绘图例程进行绘制并在 alpha 通道中具有透明度时,透明度将在您绘制的位置丢失。
您的示例代码中似乎忽略了AlphaFormat
和TransparentMode
。您还应该决定是否要在 alpha 通道或 TBitmap.Transparent
方法中使用透明度。但是我们没有位图来检查这是否是真正的问题。
下面是我的示例代码:
var lBitmap: TBitmap;
begin
lBitmap := TBitmap.Create;
lBitmap.PixelFormat := TPixelFormat.pf32bit;
lBitmap.Transparent := TRUE; // !
lBitmap.LoadFromFile( 'd:\temp\bmp32b_300dpi_transparent_400x250.bmp' );
// Bitmap RGB+Alpha created with GIMP
// modifications on pixels
Canvas.Draw(100, 0, lBitmap);
// Up to this point it is correct, the drawing is painted with transparency
lBitmap.SaveToFile( 'd:\tmp\after.bmp' ); // after this -> I have lost transparency
lBitmap.Free;
end;
如何正确使用TBitmap对象保存透明文件?
在我看来 TBitmap
不支持保存带有 alpha 通道的位图。也许我们不应该为此责怪 VCL,因为具有 alpha 透明度的 BMP 并不常见。许多应用程序不支持透明 BMP。
话虽如此,我“reverse-engineered”了一个在 GIMP 中创建的带 alpha 通道的 BMP,并编写了以下 Delphi 例程来生成完全相同的位图:
procedure SaveTransparentBitmap(ABitmap: TBitmap; const AFileName: string);
var
FS: TFileStream;
BFH: TBitmapFileHeader;
BIH: TBitmapV5Header;
y: Integer;
sl: PUInt64;
begin
// ABitmap MUST have the GIMP BGRA format.
FS := TFileStream.Create(AFileName, fmOpenWrite);
try
// Bitmap file header
FillChar(BFH, SizeOf(BFH), 0);
BFH.bfType := D42; // BM
BFH.bfSize := 4 * ABitmap.Width * ABitmap.Height + SizeOf(BFH) + SizeOf(BIH);
BFH.bfOffBits := SizeOf(BFH) + SizeOf(BIH);
FS.Write(BFH, SizeOf(BFH));
// Bitmap info header
FillChar(BIH, SizeOf(BIH), 0);
BIH.bV5Size := SizeOf(BIH);
BIH.bV5Width := ABitmap.Width;
BIH.bV5Height := ABitmap.Height;
BIH.bV5Planes := 1;
BIH.bV5BitCount := 32;
BIH.bV5Compression := BI_BITFIELDS;
BIH.bV5SizeImage := 4 * ABitmap.Width * ABitmap.Height;
BIH.bV5XPelsPerMeter := 11811;
BIH.bV5YPelsPerMeter := 11811;
BIH.bV5ClrUsed := 0;
BIH.bV5ClrImportant := 0;
BIH.bV5RedMask := [=10=]FF0000;
BIH.bV5GreenMask := [=10=]00FF00;
BIH.bV5BlueMask := [=10=]0000FF;
BIH.bV5AlphaMask := $FF000000;
BIH.bV5CSType := 524742; // BGRs
BIH.bV5Intent := LCS_GM_GRAPHICS;
FS.Write(BIH, SizeOf(BIH));
// Pixels
for y := ABitmap.Height - 1 downto 0 do
begin
sl := ABitmap.ScanLine[y];
FS.Write(sl^, 4 * ABitmap.Width);
end;
finally
FS.Free;
end;
end;
这里写一个BITMAPFILEHEADER
followed by a BITMAPV5HEADER
和BGRA格式的像素数据。
我省略了各种错误检查。例如,我不验证 ABitmap
是否确实具有所需的格式。
测试:
procedure TForm1.FormCreate(Sender: TObject);
var
bm: TBitmap;
begin
bm := TBitmap.Create;
try
bm.LoadFromFile('C:\Users\Andreas Rejbrand\Desktop\Test.bmp');
SaveTransparentBitmap(bm, 'C:\Users\Andreas Rejbrand\Desktop\Test2.bmp');
finally
bm.Free;
end;
end;
在此之后,Test.bmp
和Test2.bmp
二进制相等。
正如@Andreas Rejbrand 指出的那样,使用 alpha 通道保存 32 位位图需要一个解决方法。关于 BMP 文件格式、TBitmap.Transparent
属性 的作用以及如何使用 VCL 透明地绘制位图,似乎还有一些混淆。
32 位位图是唯一携带文件透明度信息的位图。他们在 alpha 通道中拥有该信息,而在其他任何地方都没有。在 alpha 通道中,每个像素在 RGBA 结构中都有自己的 0-255 alpha 值。这通常被称为部分透明。
当你draw/display32位位图时,你要注意TBitmap.AlphaFormat
属性。它默认为 afIgnore
,这意味着位图是在不透明的情况下绘制的。使用 afPremultiplied
或 afDefined
绘制透明。后者可能就是你想要的。
TBitmap.Transparent
属性是VCL特有的TBitmap
,BMP文件格式中没有与之对应的东西。这只是一种透明显示位图的简单方法,其中一种颜色定义了哪些像素应该完全透明。应用程序必须熟悉位图才能使用此方法。了解 TBitmap.TransparentMode
属性 的工作原理也很重要。它默认为 tmAuto
,它将位图的 bottom-leftmost 像素的颜色设置为 TBitmap.TransparentColor
。当 TransparentMode
设置为 tmFixed
时,将使用您指定的 TBitmap.TransparentColor
。此方法也可用于 32 位位图。
请注意,当您在 32 位位图上使用标准 VCL TCanvas
绘图例程进行绘制并在 alpha 通道中具有透明度时,透明度将在您绘制的位置丢失。
您的示例代码中似乎忽略了AlphaFormat
和TransparentMode
。您还应该决定是否要在 alpha 通道或 TBitmap.Transparent
方法中使用透明度。但是我们没有位图来检查这是否是真正的问题。