2011-10-11 57 views
8

默认情况下,新创建的位图似乎具有(白色)背景。至少,像素属性的查询证实。但是当Transparent设置为真时,为什么背景颜色不能用作透明颜色?如何获得位图透明度而不需要先绘制?

考虑这个简单的测试代码:

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,都具有相同的结果(无transparancy):

  • 只有设置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。

+1

如果你遵循会发生什么[这](http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/Graphics_TBitmap_TransparentMode.html )例子? –

+0

你只想画什么?你想要一个完整的透明位图绘制? – Shambhala

+0

@尚巴拉不,我想画一个(小)部分。 – NGLN

回答

8

只需设置 TransparentColor和设置位图的尺寸前 Canvas.Brush.Color。

+0

是的!而且不需要设置TransparentColor。将Canvas.Brush.Color设置为给定尺寸就可以了。谢谢。 – NGLN

+3

任何人都知道这是为什么?你甚至需要设置画笔颜色?只是强迫刷柄存在就足够了吗? –

+0

@大卫好点。 'Canvas.Brush.Handle:= 0'也很有效,很奇怪。 – NGLN

1

这将绘制一个红色正方形,其余是透明的。

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; 
+0

希望是在没有第一个FillRect的情况下得到这个结果,正如问题中明确指出的那样。 -1 – NGLN

3

我真的需要能够创建一个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文件。 然后我比较了两个文件: compating two bmp files in vbindiff on Ubuntu 所以我发现唯一的区别是添加的像素(每个都是4个字节全零RGBA),并且在头部还有其他几个字节。由于链接的I共享的格式文档,我发现它们是:FileSize(以字节为单位),BitmapWidth(以像素为单位),BitmapHeight(以像素为单位)和BitmapDataSize(以字节为单位)。最后一个是BitmapWidth*BitmapHeight*4,因为RGBA中的每个像素都是4个字节。所以现在,我可以生成alpha1p.bmp文件中看到的整个字节序列,从结尾减去4个字节(BitmapData的第1个字节),然后为每个像素的RGBA数据添加4个字节(全零) BMP我想要生成,然后回到初始序列并更新可变部分:文件大小,宽度,高度和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; 

这是自拉撒路维基复制:http://wiki.freepascal.org/Writing_portable_code_regarding_the_processor_architecture你可以跳过这一部分,如果你不与大尾端平台交易,或者你可以使用编译器IFDEF指令。问题是,如果你使用{$IFDEF ENDIAN_BIG}那么编译器就是这样,而函数实际上是测试系统。这在链接的wiki中有解释。

使用范例

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; 
+0

关于Quora我建议在SDL中采用此方法:https://www.quora.com/A-simple-way-to-create-a-transparent-surface-with-SDL-Pascal/answer/Krzysztof-Kamil-Jacewicz –

+0

我不认为这个问题是关于32位* alpha *透明位图。无论如何,好主意。但不会如预期的那样工作?:https://pastebin.com/EXDRu3zG – kobik

+0

@kobik:您的方法在某些场合对我无效。我认为它不能在Linux上使用gtk widgetset。 –