1
votes

After going through several other related questions I couldn't come up with a working code for this, so please spare the "duplicate question" tags.

Given a PNG image with either per-pixel alpha channel or single-color transparency, I need code to draw it onto a TBitmap32 which already contains an image (some drawing goes on before the PNG part). So let's say my TBitmap32 is 200x200, I do some drawing on it, then I want to ~insert a smaller transparent PNG image on top of its current content, transparently according to the PNG's alpha channel data or single-color alpha.

Uses pngimage, GR32;

procedure TForm1.Button1Click(Sender: TObject);
Var b: TBitmap;
    b32: TBitmap32;
    p: TPngImage;
begin
  b   := TBitmap.Create;
  b32 := TBitmap32.Create;
  p   := TPngImage.Create;

  // 50x50 PNG
  p.LoadFromFile('z:\test2.png');

  b.Width    := 200;
  b.Height   := 200;
  b32.Width  := 200;
  b32.Height := 200;
  // some drawing happens on the b32~

  // insert code here to draw the png onto the b32, on top of
  // what's already drawn, and at specific coordinates i.e 10,10


  /////////////////////////////

  b32.DrawTo(b.Canvas.Handle,0,0);
  Canvas.Draw(0,0,b);

  p.Free;
  b32.Free;
  b.Free;
end;

Original PNG:

enter image description here

Results so far:

enter image description here

1
So what is not as you would expect? I see green picture nicely blended in to black background so PNG alpha transparency is maintained. Is problem that black background? If it is have you filled your entire bm32 bitmap with transparent color before doing any drawing on it. I assume default color of new bitmap might be either black or white and probably with full opacity.SilverWarior
The black area should be transparent, blended on the other bitmap32 below (red)hikari
Nvm it.. I'm stupid, the most obvious and simple step did the job unexpectedly: p.Draw(b32.Canvas,Rect(50,100,150,200)); Delphi Seattle's version of TPngImage does the job perfectly: i.imgur.com/yb56ezN.pnghikari

1 Answers

2
votes

There are two ways of working with transparent PNG files:

  1. Load them into intermediary TBitmap32 bitmaps and then manipulate these TBitmap32 bitmaps.
  2. Use TPngImage.Draw (implemented in Vcl.Imaging.pngimage with Delphi XE2 and later) directly on target Canvas, as you have pointed out.

The second way is preferable when it comes to transparency, because the code that you may find to load a PNG into TBitmap32 may work incorrectly. Here are the two examples of the incorrect code that is used most frequently:

(1) “LoadPNGintoBitmap32” from http://graphics32.org/wiki/FAQ/ImageFormatRelated - it applies the transparency twice, so the images with alpha values other than 0 or 255 will look differently than in other software (most noticeable on translucent images with glass effects). This code will first apply alpha to RGB and then sets alpha, so when you pain, alpha will be applied again. You can find more information on this issue here: Delphi, GR32 + PngObject: converting to Bitmap32 doesn't work as expected . Besides that, it doesn't convert correctly transparency from paletted images into the alpha layer of TBitmap32, for example, all white pixels become transparent.

(2) “LoadBitmap32FromPNG” from gr32ex library: https://code.google.com/archive/p/gr32ex/ - a slightly different implementation of the same algorithm as (1), and has the same issues as (1).

If you still prefer using TBitmap32, make the following sequence of steps:

  1. Make sure your code correctly converts PNG to TBitmap32.
  2. Do not use TBitmap32 with transparent images to draw directly on HDC, Canvas or TBitmap. Use dmBlend and DrawTo or BlockTransfer() to draw on another TBitmap32. For example, to draw transparently on a TBitmap, create an intermediary cache TBitmap32:
    1. Copy the image from TBitmap to the cache TBitmap32;
    2. Apply your transparent image to the cache TBitmap32 using DrawTo or BlockTransfer(), avoid using Canvas or HDC to mix two images because they lose alpha layer information;
    3. Copy the image back from the cache TBitmap32 to your TBitmap.