[英]How to get bitmap transparancy without the need to paint first?
默认情况下,新创建的位图似乎具有(白色)背景。 至少,对Pixels属性的查询确认。 但是,当Transparent
设置为true时,为什么背景颜色不用作透明颜色?
考虑这个简单的测试代码:
procedure TForm1.Button1Click(Sender: TObject);
var
Bmp: TBitmap;
begin
Bmp := TBitmap.Create;
try
Bmp.Width := 100;
Bmp.Height := 100;
Bmp.Transparent := True;
Canvas.Draw(0, 0, Bmp); // A white block is drawn
Bmp.Canvas.Brush.Color := Bmp.Canvas.Pixels[0, 99]; // = 'clWhite'
Bmp.Canvas.FillRect(Rect(0, 0, 100, 100));
Canvas.Draw(0, 100, Bmp); // "Nothing" is drawn
finally
Bmp.Free;
end;
end;
出于某种原因,整个位图表面必须先涂上它才能显得透明,这听起来很奇怪。
尝试以下变体来消除对FillRect
的调用,所有调用都具有相同的结果(没有透明度):
Brush.Color
, Brush.Handle := CreateSolidBrush(clWhite)
, PixelFormat := pf32Bit
, IgnorePalette := True
, TransparantColor := clWhite
, TransparantMode := tmFixed
, Canvas.Pixels[0, 99] := clWhite
只使像素透明, Modified := True
。 因此,希望仅绘制新创建的位图的一部分并使剩余的表面透明。
使用:Delphi 7,Win 7/64。
刚设置 TransparentColor和 在设置位图的尺寸之前使用Canvas.Brush.Color。
我真的需要能够以32位RGBA格式创建任意大小的完全透明(以及其他空/空)TBitmap。 多次。 Lazarus能够将这样的位图加载到TBitmap中,并且在加载之后,您可以使用scanline和不使用RGBA格式来操作它。 但是当你自己创建TBitmap时它就不起作用了。 像素格式似乎完全被忽略了。 所以我所做的就是开箱即用,简单,它几乎是AMUZING(!)。 但它很实用,效果非常好,完全独立于LCL和任何第三方库。 即使不依赖于Graphics单元,因为它生成实际的32位RGBA BMP文件(我生成它到TMemoryStream,你可以生成不同的)。 然后,一旦你拥有它,你可以使用TBitmap.LoadFrom源或TPicture.LoadFrom源加载它。
我最初想要按照这里描述的格式生成格式正确的BMP文件的背景故事 : http : //www.fileformat.info/format/bmp/egff.htm但是BMP格式的变种很少,我不清楚我应该遵循哪一个。 所以我决定采用逆向工程方法,但格式描述后来帮助了我。 我使用图形编辑器(我使用GIMP)来创建一个空的1x1像素32 RGBA BMP文件,并将其命名为alpha1p.bmp,它只包含透明度。 然后我将画布调整为10x10像素,并保存为alpha10p.bmp文件。 然后我比较了两个文件: 在Ubuntu上的vbindiff中编写两个bmp文件所以我发现唯一的区别是添加的像素(每个都是4个字节全部为零RGBA),并且标头中的其他字节很少。 由于链接我共享的格式文档,我发现它们是: FileSize
(以字节为单位), BitmapWidth
(以像素为单位), BitmapHeight
(以像素为单位)和BitmapDataSize
(以字节为单位)。 最后一个是BitmapWidth*BitmapHeight*4
,因为RGBA中的每个像素都是4个字节。 所以现在,我可以生成在alpha1p.bmp文件中看到的整个字节序列,从末尾减去4个字节( BitmapData
的第1个),然后为每个像素添加4个字节(全零)的RGBA数据。 BMP我想生成,然后回到初始序列并更新变量部分:FileSize,width,height和BMP数据大小。 它完美无瑕! 我只需要为BigEndian添加测试,并在写入之前交换单词和双字。 这将成为BigEndian中运行的ARM平台的问题。
代码
const
C_BLANK_ALPHA_BMP32_PREFIX : array[0..137]of byte
= ($42, $4D, $00, $00, $00, $00, $00, $00, $00, $00, $8A, $00, $00, $00, $7C, $00,
$00, $00, $0A, $00, $00, $00, $0A, $00, $00, $00, $01, $00, $20, $00, $03, $00,
$00, $00, $90, $01, $00, $00, $13, $0B, $00, $00, $13, $0B, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $FF, $00, $00, $FF, $00, $00, $FF,
$00, $00, $FF, $00, $00, $00, $42, $47, $52, $73, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $02, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00, $00, $00, $00, $00 );
(...)
Function RenderEmptyAlphaBitmap(AWidth,AHeight: integer): TMemoryStream;
var
buf : array[1..4096]of byte;
i,p : int64;
w : word;
dw : dword;
BE : Boolean;
begin
buf[low(buf)] := $00; //this is jyst to prevent compiler warning about not initializing buf variable
Result := TMemoryStream.Create;
if(AWidth <1)then AWidth := 1;
if(AHeight<1)then AHeight := 1;
//Write File Header:
Result.Write(C_BLANK_ALPHA_BMP32_PREFIX, SizeOf(C_BLANK_ALPHA_BMP32_PREFIX));
//Now start writing the pixels:
FillChar(buf[Low(buf)],Length(buf),$00);
p := Result.Position;
Result.Size := Result.Size+int64(AWidth)*int64(AHeight)*4;
Result.Position := p;
i := int64(AWidth)*int64(AHeight)*4; //4 because RGBA has 4 bytes
while(i>0)do
begin
if(i>Length(buf))
then w := Length(buf)
else w := i;
Result.Write(buf[Low(buf)], w);
dec(i,w);
end;
//Go back to the original header and update FileSize, Width, Height, and offset fields:
BE := IsBigEndian;
Result.Position := 2; dw := Result.Size;
if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw));
Result.Position := 18; dw := AWidth;
if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw));
Result.Position := 22; dw := AHeight;
if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw));
Result.Position := 34; dw := AWidth*AHeight*4;
if BE then SwapEndian(dw); Result.Write(dw, SizeOf(dw));
//Done:
Result.Position := 0;
end;
注意C_BLANK_ALPHA_BMP32_PREFIX
常量基本上是来自我的样本alpha1p.bmp文件的字节序列的副本,减去最后4个字节,即RGBA像素。 :d
另外,我使用的是IsBigEndian
函数,如下所示:
Function IsBigEndian: Boolean;
type
Q = record case Boolean of
True : (i: Integer);
False : (p: array[1..4] of Byte);
end;
var
x : ^Q;
begin
New(x);
x^.i := 5;
Result := (x^.p[4]=5);
Dispose(x);
end;
这是从Lazarus Wiki复制的: http ://wiki.freepascal.org/Writing_portable_code_regarding_the_processor_architecture如果你不处理BigEndian平台,你可以跳过这部分,或者你可以使用编译器IFDEF指令。 问题是如果你使用{$IFDEF ENDIAN_BIG}
那么它就是编译器的情况,而函数实际上是测试系统的。 这在链接的维基中解释。
样品用法
Procedure TForm1.Button1Click(Sender: TObject);
var
MS : TMemoryStream;
begin
MS := RenderEmptyAlphaBitmap(Image1.Width, Image1.Height);
try
if Assigned(MS)then Image1.Picture.LoadFromStream(MS);
//you can also MS.SaveToFile('my_file.bmp'); if you want
finally
FreeAndNil(MS);
end;
end;
这将绘制一个红色方块,其余的是透明的。
procedure TForm1.btnDrawClick(Sender: TObject);
var
Bmp: TBitmap;
begin
Bmp := TBitmap.Create;
try
Bmp.Width := 100;
Bmp.Height := 100;
Bmp.Transparent := TRUE;
Bmp.TransparentColor := clWhite;
Bmp.Canvas.Brush.Color := clWhite;
Bmp.Canvas.FillRect(Rect(0, 0, Bmp.Width, Bmp.Height));
Bmp.Canvas.Brush.Color := clRed;
Bmp.Canvas.FillRect(Rect(42, 42, 20, 20));
Canvas.Draw(12, 12, Bmp);
finally
Bmp.Free;
end;
end;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.