2011-11-21 20 views
5

Tôi đang sử dụng GR32 để vẽ nhiều hình ảnh PNG bán trong suốt. Cho đến nay tôi đã sử dụng các phương pháp sau đây:Delphi, GR32 + PngObject: chuyển đổi sang Bitmap32 không hoạt động như mong đợi

png:= TPNGObject.Create; 
    png.LoadFromFile(...); 
    PaintBox321.Buffer.Canvas.Draw(120, 20, png); 

tuy nhiên tôi muốn chuyển sang phương pháp đề xuất trên trang web GR32 (http://graphics32.org/wiki/FAQ/ImageFormatRelated):

tmp:= TBitmap32.Create; 
    LoadPNGintoBitmap32(tmp, ..., foo); 
    tmp.DrawMode:= dmBlend; 
    PaintBox321.Buffer.Draw(Rect(20, 20, 20+ tmp.Width, 20+tmp.Height), 
    tmp.ClipRect, tmp); 

Trong khi phương pháp đầu tiên làm việc hoàn toàn tốt đẹp, thứ hai - cho kết quả tương tự - gây ra vấn đề rất lạ với kênh alpha, xem hình ảnh (cũng cho thấy so sánh với cùng một hình ảnh "sắp xếp" trong Paint.NET - cả nền và biểu tượng được mở trên các lớp của trình soạn thảo). Hình ảnh mô tả Bitmap32 được tải hoặc vẽ không đúng cách. Có lời khuyên nào không?

Problem with TBitmap32 alpha channel

- thêm 22 tháng 11

Tôi đã phát hiện ra rằng nó không phải là về vẽ, đó là về tải PNG để BMP32. Lưu lại từ BMP32 sang PNG tạo ảnh không chính xác, "đã được làm mờ" (ảnh ở bên trái) PNG.

+1

Tôi cho rằng đó là do "độ mờ", bạn cũng đã đặt "tmp.DrawMode: = dmBlend;", tôi chưa sử dụng GR32, nhưng tôi đoán rằng sự khác biệt là do độ mờ. – ComputerSaysNo

+1

@Dorin Duminica, không phải vậy. Ví dụ trên trang web của họ cho thấy rằng chế độ sẽ là dmBlend nếu có bất kỳ sự minh bạch nào trong hình ảnh PNG được tải. Vì tôi biết tất cả hình ảnh của tôi đều trong suốt nên tôi không phải kiểm tra. – migajek

Trả lời

9

Lý do có vẻ là độ trong suốt được áp dụng hai lần cho hình ảnh khi được tải với LoadPNGintoBitmap32, mang lại cho nó một cái nhìn minh bạch hơn và màu xám (chi tiết về điều này sau).

Đầu tiên là tính minh bạch:

Đây là mã từ bản gốc LoadPNGintoBitmap32, các bộ phận quan trọng được đánh dấu bằng nhận xét:

PNGObject := TPngObject.Create; 
PNGObject.LoadFromStream(srcStream); 

destBitmap.Assign(PNGObject); // <--- paint to destBitmap's canvas with transparency (!) 
destBitmap.ResetAlpha;   

case PNGObject.TransparencyMode of // <--- the following code sets the transparency again for the TBitmap32 
{ ... } 

Các destBitmap.Assign nội bộ không giống như bạn trong cách tiếp cận trước đây của bạn: nó cho phép của hình ảnh PNG tự vẽ lên khung hình của nó. Hoạt động này tôn trọng kênh alpha của PNG. Nhưng điều này là không cần thiết, vì kênh alpha được gán cho các pixel của TBitmap32 trong bước thứ hai!

Bây giờ thay đổi mã như sau, các bộ phận quan trọng được một lần nữa đánh dấu với ý kiến:

PNGObject := TPngObject.Create; 
PNGObject.LoadFromStream(srcStream); 

PNGObject.RemoveTransparency; // <--- paint PNG without any transparency... 
destBitmap.Assign(PNGObject); // <--- ...here 
destBitmap.ResetAlpha; 

srcStream.Position:=0; 
PNGObject.LoadFromStream(srcStream); // <--- read the image again to get the alpha channel back 

case PNGObject.TransparencyMode of // <--- this is ok now, the alpha channel now only exists in the TBitmap32 
{ ... } 

Các giải pháp trên không hiệu quả bởi vì nó đọc hình ảnh hai lần. Nhưng nó cho thấy lý do tại sao phương pháp tiếp cận thứ hai của bạn tạo ra một hình ảnh minh bạch hơn.

Và đối với màu xám: Có một vấn đề nữa trong mã ban đầu: destBitmap.Assign trước hết điền vào nền với clWhite32, sau đó vẽ hình ảnh trong suốt lên đó. Và sau đó LoadPNGintoBitmap32 xuất hiện và thêm một lớp minh bạch khác lên trên nó.

+0

cảm ơn bạn, đây phải là một câu trả lời hoàn hảo - không chỉ là mã đã sửa, mà còn giải thích chi tiết về những gì đã sai :) – migajek

+1

Hehe, bạn được chào đón :) Đó là một câu hỏi thú vị và được trình bày tốt! –

0

Sự cố có thể là PNG đã chuyển đổi thành TBitmap32 không chính xác, mất thông tin trong suốt khi chuyển tiếp. Nó là một trường hợp phổ biến với hình ảnh PNG paletted. Nếu không, bạn sẽ không phải sử dụng „Bitmap.DrawMode: = dmTransparent” và „OuterColor”. Nếu thông tin transparencry từ PNG sẽ được chuyển đúng vào TBitmpa32, DrawMode: = dmBlend sẽ làm việc, mà không cần phải đặt OuterColor.

Điều quan trọng nhất là cách bạn tải PNG vào TBitmap32. TPngImage từ Vcl.Imaging.đơn vị pngimage (được triển khai trong Delphi XE2 và sau này) có thể vẽ một cách rõ ràng trên bitmap, bảo toàn những gì trên bitmap đó, kết hợp màu sắc bằng cách sử dụng lớp alpha alpha, v.v.) vào thành phần alpha của mỗi pixel của TBitmap32. Khi TPngImage đã vẽ một hình ảnh, bạn nhận được RGB kết hợp cho mỗi pixel, nhưng thành phần alpha không được chuyển sang bitmap đích.

Có thói quen helper sẵn mà cố gắng để tải một PNG vào một TBitmap32 với tính minh bạch, nhưng họ có nhiều nhược điểm:

(1) “LoadPNGintoBitmap32” từ http://graphics32.org/wiki/FAQ/ImageFormatRelated - nó áp dụng tính minh bạch hai lần, vì vậy những hình ảnh với giá trị alpha khác 0 hoặc 255 sẽ khác với các phần mềm khác (đáng chú ý nhất trên hình ảnh mờ với hiệu ứng kính). Trước tiên, mã này sẽ áp dụng alpha thành RGB và sau đó đặt alpha làm lớp riêng biệt, vì vậy khi bạn vẽ, alpha sẽ được áp dụng lại. Bạn có thể tìm thêm thông tin về vấn đề này tại đây: Delphi, GR32 + PngObject: converting to Bitmap32 doesn't work as expected . Bên cạnh đó, nó không chuyển đổi một cách chính xác độ trong suốt từ các hình ảnh được paletted thành lớp alpha của TBitmap32. Chúng tự thiết lập độ trong suốt cho các pixel của một màu nhất định của bitmap đầu ra (được render thành RGB) thay vì làm trước khi render sang RGB, vì vậy độ trong suốt thực sự bị mất như trên ảnh mẫu của bạn khi tất cả các pixel trắng trong suốt.

(2) “LoadBitmap32FromPNG” từ thư viện gr32ex: https://code.google.com/archive/p/gr32ex/ - triển khai hơi khác cùng một thuật toán như (1) và có cùng vấn đề với (1).

Vì vậy, giải pháp là:

  1. Không sử dụng TBitmap32; sử dụng Vcl.Imaging.pngimage.TPngImage vẽ trực tiếp trên bitmap đích (màn hình, vv) - đây là cách tương thích nhất để giao dịch chính xác với các định dạng PNG khác nhau.
  2. Sử dụng định tuyến trợ giúp để chuyển thông tin minh bạch từ Vcl.Imaging.pngimage.TPngImage sang TBitmap32.
  3. Sử dụng thư viện PNG GR32 có thể tải một PNG vào TBitmap32 https://sourceforge.net/projects/gr32pnglibrary/ Vì bây giờ bạn có tất cả thông tin về vấn đề này, bạn có thể nhận được giải pháp phù hợp với mình.

Làm thế nào để tải các lớp alpha trong một đường chuyền

Heinrich Ulbricht làm một gợi ý tốt đẹp để loại bỏ các lớp minh bạch trước paining và sau đó để đọc hình ảnh một lần nữa. Để tránh tải hình ảnh hai lần, bạn có thể lưu lớp alpha trước khi gọi PNGObject.RemoveTransparency. Đây là mã áp dụng chính xác lớp alpha và chỉ tải hình ảnh một lần. Thật không may, nó không hoạt động với hình ảnh paletted. Nếu bạn biết cách điền chính xác lớp alpha của TBitmap32 từ bất kỳ hình ảnh paletted nào, mà không có các hiệu ứng được mô tả tại Transparent Png to TBitmap32 hãy cho tôi biết.

procedure LoadPNGintoBitmap32(DstBitmap: TBitmap32; SrcStream: TStream; out AlphaChannelUsed: Boolean); 
var 
    PNGObject: TPngImage; 
    PixelPtr: PColor32; 
    AlphaPtr: PByte; 
    SaveAlpha: PByte; 
    I, AlphaSize: Integer; 
begin 
    AlphaChannelUsed := False; 
    PNGObject := TPngImage.Create; 
    try 
    PNGObject.LoadFromStream(SrcStream); 
    AlphaPtr := PByte(PNGObject.AlphaScanline[0]); 
    if Assigned(AlphaPtr) then 
    begin 
     AlphaSize := PNGObject.Width * PNGObject.Height; 
     if AlphaSize <= 0 then raise Exception.Create('PNG files with zero dimensions are not supported to be loaded to TBitmap32'); 
     GetMem(SaveAlpha, AlphaSize); 
     try 
     Move(AlphaPtr^, SaveAlpha^, AlphaSize); 
     PNGObject.RemoveTransparency; 
     DstBitmap.Assign(PNGObject); 
     DstBitmap.ResetAlpha; 
     PixelPtr := PColor32(@DstBitmap.Bits[0]); 
     AlphaPtr := SaveAlpha; 
     for I := 0 to AlphaSize-1 do 
     begin 
      PixelPtr^ := (PixelPtr^ and $00FFFFFF) or (TColor32(AlphaPtr^) shl 24); 
      Inc(PixelPtr); 
      Inc(AlphaPtr); 
     end; 
     finally 
     FreeMem(SaveAlpha, AlphaSize); 
     end; 
     AlphaChannelUsed := True; 
    end else 
    if PNGObject.TransparencyMode = ptmNone then 
    begin 
     DstBitmap.Assign(PNGObject); 
    end else 
    begin 
     raise Exception.Create('Paletted PNG images are not supported in LoadPNGintoBitmap32, transparency cannot be stored to TBitmap32'); 
    end; 
    finally 
    FreeAndNil(PNGObject); 
    end; 
end; 
Các vấn đề liên quan