如何正确使用TBitmap对象以透明方式保存文件?

问题描述

下面是我的示例代码:

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,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。

这就是说,我“反向工程化”了在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 := $4D42;  // 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 :=   $00FF0000;
    BIH.bV5GreenMask := $0000FF00;
    BIH.bV5BlueMask :=  $000000FF;
    BIH.bV5AlphaMask := $FF000000;
    BIH.bV5CSType := $73524742; // 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,后跟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.bmpTest2.bmp是二进制相等的。

,

@Andreas Rejbrand指出,保存带有Alpha通道的32位位图需要一种变通方法。对于BMP文件格式,TBitmap.Transparent属性的作用以及如何使用VCL透明地绘制位图,似乎还有些困惑。

32位位图是唯一包含有关文件透明度的信息的位图。他们在alpha通道中拥有该信息,而在其他任何地方都没有。在alpha通道中,每个像素在RGBA结构中都有自己的0-255 alpha值。这通常称为部分透明。

当绘制/显示32位位图时,必须注意TBitmap.AlphaFormat属性。它的默认值为afIgnore,这意味着位图的绘制没有透明度。使用afPremultipliedafDefined进行透明绘制。后者可能就是您想要的。

TBitmap.Transparent属性特定于VCL TBitmap,并且BMP文件格式中没有与其对应的任何内容。这只是透明显示位图的简单方法,其中颜色定义了哪些像素应该是完全透明的。应用程序必须熟悉位图才能使用此方法。注意TBitmap.TransparentMode属性的工作方式也很重要。它默认为tmAuto,它将位图的最左下像素的颜色设置为TBitmap.TransparentColor。将TransparentMode设置为tmFixed时,将使用您指定的TBitmap.TransparentColor。此方法也可以在32位位图上使用。

请注意,当您在32位位图上使用标准VCL TCanvas进行绘制的例程在alpha通道中具有透明性时,透明性将在您绘制的位置丢失。

在示例代码中,您似乎忽略了AlphaFormatTransparentMode。您还应该决定是否要在Alpha通道或TBitmap.Transparent方法中使用透明度。但是我们没有位图来检查这是否是真正的问题。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...