2011-10-11 98 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。 –