2011-10-11 8 views
8

Một bitmap mới được tạo dường như có nền (màu trắng) theo mặc định. Ít nhất, một truy vấn trên thuộc tính Pixels xác nhận. Nhưng tại sao màu nền không được sử dụng làm màu trong suốt khi Transparent được đặt đúng?Làm thế nào để có được bitmap trong suốt mà không cần phải sơn đầu tiên?

xem xét mã này kiểm tra đơn giản:

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; 

Đối với một số lý do, toàn bộ bề mặt bitmap phải được vẽ trước khi nó có thể xuất hiện trong suốt, mà âm thanh loại lẻ.

Các biến thể sau đây đang cố gắng để loại bỏ các cuộc gọi đến FillRect, tất cả với kết quả tương tự (không transparancy):

  • Chỉ thiết Brush.Color,
  • Brush.Handle := CreateSolidBrush(clWhite),
  • PixelFormat := pf32Bit,
  • IgnorePalette := True ,
  • TransparantColor := clWhite,
  • TransparantMode := tmFixed,
  • Canvas.Pixels[0, 99] := clWhite chỉ làm cho pixel trong suốt,
  • Modified := True.

Vì vậy, mong muốn chỉ vẽ một phần của bitmap mới được tạo và nhận được bề mặt còn lại trong suốt.

Sử dụng: Delphi 7, Win 7/64.

+1

gì sẽ xảy ra nếu bạn làm theo [này] (http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/Graphics_TBitmap_TransparentMode.html) thí dụ ? –

+0

Bạn có muốn vẽ gì không? Bạn có muốn một Bitmap trong suốt hoàn chỉnh được vẽ không? – Shambhala

+0

@Shamballa Không, tôi muốn vẽ một phần nhỏ. – NGLN

Trả lời

8

Chỉ cần thiết lập TransparentColor và Canvas.Brush.Color trước khi thiết lập kích thước của bitmap.

+0

Có! Và không cần thiết lập TransparentColor. Thiết lập Canvas.Brush.Color befóre cho kích thước hiện các trick. Cảm ơn bạn. – NGLN

+3

Bất cứ ai biết tại sao điều này là như vậy? Bạn thậm chí cần phải thiết lập màu sắc bàn chải? Có đủ chỉ để buộc tay cầm bàn chải tồn tại? –

+0

@David Điểm tốt. 'Canvas.Brush.Handle: = 0' cũng hoạt động, kỳ lạ đủ. – NGLN

1

Điều này sẽ vẽ một hình vuông màu đỏ và phần còn lại trong suốt.

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

Ước muốn là để có được kết quả này mà không có sự fillRect đầu tiên, như đã nêu rõ trong câu hỏi. -1 – NGLN

3

Tôi thực sự cần thiết để có thể tạo một TBitmap hoàn toàn minh bạch (và nếu không trống/trống) có kích thước tùy ý ở định dạng 32 bit RGBA. Nhiều lần. Lazarus có thể tải bitmap như vậy vào TBitmap và sau khi nó được tải, bạn có thể thao tác nó với scanline và những gì không sử dụng định dạng RGBA. Nhưng nó không hoạt động khi bạn tạo bản thân TBitmap. Định dạng pixel dường như bị bỏ qua hoàn toàn. Vì vậy, những gì tôi đã làm là như vậy out-of-the box, và đơn giản, rằng nó gần như AMUZING (!). Nhưng nó là thực tế, hoạt động siêu đẹp, và hoàn toàn độc lập với LCL và bất kỳ thư viện của bên thứ ba nào. Thậm chí không phụ thuộc vào đơn vị đồ họa, bởi vì nó tạo ra các tập tin 32 bit RGBA BMP thực tế (tôi tạo ra nó để TMemoryStream, bạn có thể tạo ra một cách khác nhau). Sau đó, một khi bạn có nó, ở nơi khác trong mã của bạn, bạn chỉ có thể tải nó bằng cách sử dụng nguồn TBitmap.LoadFrom nguồn hoặc TPicture.LoadFrom.

Câu chuyện nền ban đầu tôi muốn tạo ra định dạng đúng tập tin BMP sau định dạng như mô tả ở đây: http://www.fileformat.info/format/bmp/egff.htm Nhưng có vài biến thể của định dạng BMP, và tôi đã không rõ ràng về cái nào tôi phải làm theo . Vì vậy, tôi quyết định đi với một cách tiếp cận kỹ thuật đảo ngược, nhưng mô tả định dạng đã giúp tôi sau này. Tôi đã sử dụng một trình soạn thảo đồ họa (tôi đã sử dụng GIMP) để tạo một tệp ảnh RGB1 32 RGBA BMP trống rỗng, và được gọi là alpha1p.bmp, nó chỉ chứa trong suốt không có gì khác. Sau đó, tôi đã đổi kích thước canvas thành 10x10 pixel và được lưu dưới dạng alpha10p.tệp bmp. Sau đó, tôi so sánh hai tệp: compating two bmp files in vbindiff on Ubuntu Vì vậy, tôi phát hiện ra rằng sự khác biệt duy nhất là pixel được thêm (mỗi pixel là 4 byte tất cả các số không RGBA) và một vài byte khác trong tiêu đề. Bởi vì các tài liệu định dạng tại liên kết tôi chia sẻ, tôi đã tìm ra rằng đó là: FileSize (theo byte), BitmapWidth (tính bằng pixel), BitmapHeight (tính bằng pixel) và BitmapDataSize (theo byte). Cái cuối cùng là BitmapWidth*BitmapHeight*4, bởi vì mỗi pixel trong RGBA là 4 byte. Vì vậy, bây giờ, tôi chỉ có thể tạo ra rằng toàn bộ chuỗi các byte như đã thấy bên trong alpha1p.bmp file, trừ đi 4 byte từ ngày kết thúc (ngày 1 của BitmapData), sau đó thêm 4 byte (tất cả zero) của dữ liệu RGBA cho mỗi điểm ảnh của BMP Tôi muốn tạo ra, sau đó quay trở lại trình tự ban đầu và cập nhật các phần biến: FileSize, chiều rộng, chiều cao và kích thước dữ liệu BMP. Và nó hoạt động hoàn hảo! Tôi chỉ cần thêm bài kiểm tra cho BigEndian và sẽ đổi số từ và dword trước khi viết. Điều đó sẽ trở thành vấn đề trên nền tảng ARM hoạt động trong BigEndian.

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; 

Chú ý cách C_BLANK_ALPHA_BMP32_PREFIX liên tục về cơ bản là bản sao của chuỗi byte từ file mẫu alpha1p.bmp của tôi, trừ đi 4 byte cuối cùng, đó là những điểm ảnh RGBA. : D

Ngoài ra, tôi đang sử dụng IsBigEndian chức năng mà đi như thế này:

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; 

này được sao chép từ Lazarus Wiki: http://wiki.freepascal.org/Writing_portable_code_regarding_the_processor_architecture bạn có thể bỏ qua phần này nếu bạn không đối phó với các nền tảng BigEndian, hoặc bạn có thể sử dụng một trình biên dịch IFDEF chỉ thị. Cái này là nếu bạn sử dụng {$IFDEF ENDIAN_BIG} sau đó nó là những gì trình biên dịch mọi thứ là trường hợp, trong khi chức năng thực sự kiểm tra hệ thống. Điều này được giải thích trong wiki được liên kết.

sử dụng mẫu

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

trên Quora Tôi đề xuất áp dụng phương pháp này với SDL: https://www.quora.com/A-simple-way-to-create-a-transparent-surface-with-SDL-Pascal/answer/Krzysztof-Kamil-Jacewicz –

+0

tôi không nghĩ rằng câu hỏi là về 32bit * alpha * bitmap trong suốt. trong mọi trường hợp, ý tưởng hay. nhưng điều này không hoạt động như mong đợi ?: https://pastebin.com/EXDRu3zG – kobik

+0

@kobik: phương pháp của bạn không hoạt động đối với tôi trong một số trường hợp. Tôi nghĩ rằng nó không hoạt động được trên Linux bằng cách sử dụng gtk widgetset. –