Search code examples
imagedelphipnggraphicex

Can't get how to draw PNG properly via GraphicEx in Delphi


I have a little problem with PNG format. To read and display PNG files I use GraphicEx library by Mike Lischke (got it there). All was good before I decided to draw PNG file with transparent background.
I use this code to load and draw PNG on form's canvas:

procedure TForm1.aButton1Click(Sender: TObject);
var
  PNGGraph: GraphicEx.TPNGGraphic;
begin
  PNGGraph := GraphicEx.TPNGGraphic.Create;

  PNGGraph.PixelFormat := pf32bit; - added code line

  PNGGraph.LoadFromFile('demo.png');
  Form1.Canvas.Draw(10, 10, PNGGraph);
  PNGGraph.Free;
end;

What I get you can see on a picture below:
png without premultiplied alpha

After hours searching in Internet, I found that I should multiple alpha channel. I get some code from here (Mike Sutton's answer): Fade in an alpha-blended PNG form in Delphi

procedure PreMultiplyBitmap(Bitmap: TBitmap);
var
  Row, Col: integer;
  p: PRGBQuad;
  PreMult: array[byte, byte] of byte;
begin
  // precalculate all possible values of a*b
  for Row := 0 to 255 do
    for Col := Row to 255 do
    begin
      PreMult[Row, Col] := Row*Col div 255;
      if (Row <> Col) then
        PreMult[Col, Row] := PreMult[Row, Col]; // a*b = b*a
    end;

  for Row := 0 to Bitmap.Height-1 do
  begin
    Col := Bitmap.Width;
    p := Bitmap.ScanLine[Row];
    while (Col > 0) do
    begin
      p.rgbBlue := PreMult[p.rgbReserved, p.rgbBlue];
      p.rgbGreen := PreMult[p.rgbReserved, p.rgbGreen];
      p.rgbRed := PreMult[p.rgbReserved, p.rgbRed];
      inc(p);
      dec(Col);
    end;
  end;
end;

Using this code I got a little odd result:
enter image description here

The picture above has black background and in the same time looks almost as an original image.

On a picture below you can see an original PNGimage:
enter image description here

So, my question is: how to draw PNG file correctly with transparency and without black background?

I looked into GraphicEx's units, but can't get enough info about my question. Can't believe that such serious graphic library as GraphicEx is not able to draw PNG files without any troubles.

P.S.
Bitmap property Transparent doesn't work properly - black background still on a picture.

Thanks to everyone who can give me advice!

EDIT
When I set PixelFormat = pf32bit, it makes bitmap 'broken' visually.
Picture below demonstrates this effect:
Artifacts on a bitmap


Solution

  • The problem is that Mike's PNG graphic doesn't support drawing transparency.

    procedure TForm1.PaintBox1Paint(Sender: TObject);
    var
        g: TGraphic;
    begin
        g := TPNGGraphic.Create;
        g.LoadFromFile('D:\Temp\FolderOpen_48x48_72.png');
        PaintBox1.Canvas.Draw(0, 0, g);
    end;
    

    Comes out without the alpha channel being taken into account:

    enter image description here

    TPNGObject

    For Delphi 2005 use can use Gustavo Daud's pngdelphi library (It is the class that was later absorbed into Delphi). It fully supported drawing with alpha blending:

    procedure TForm1.PaintBox1Paint(Sender: TObject);
    var
        g: TGraphic;
    begin
    //  g := TPNGGraphic.Create;
        g := TPNGObject.Create;
        g.LoadFromFile('D:\Temp\FolderOpen_48x48_72.png');
        PaintBox1.Canvas.Draw(0, 0, g);
    end;
    

    It draws correctly:

    enter image description here

    Windows Imaging Component

    I don't know when Borland added Windows Imaging Component (WIC) to Delphi. But in Delphi 5 i translated the headers myself, and created a TGraphic that uses WIC to perform all the work: TWicGraphic:

    procedure TForm1.PaintBox1Paint(Sender: TObject);
    var
        g: TGraphic;
    begin
    //  g := TPNGGraphic.Create;
    //  g := TPNGObject.Create;
        g := TWicGraphic.Create;
        g.LoadFromFile('D:\Temp\FolderOpen_48x48_72.png');
        PaintBox1.Canvas.Draw(0, 0, g);
    end;
    

    It also paints correctly:

    enter image description here

    GDI+

    There's also GDI+. I also don't know when Borland added support for GDI+ to Delphi. But in Delphi 5 i translated GDI+ myself and created a TGraphic that uses GDI+ for all the work, TGDIPlusGraphic:

    procedure TForm1.PaintBox1Paint(Sender: TObject);
    var
        g: TGraphic;
    begin
    //  g := TPNGGraphic.Create;
    //  g := TPNGObject.Create;
    //  g := TWicGraphic.Create;
        g := TGDIPlusGraphic.Create;
        g.LoadFromFile('D:\Temp\FolderOpen_48x48_72.png');
        PaintBox1.Canvas.Draw(0, 0, g);
    end;
    

    it also draws correctly:

    enter image description here


    But to answer your question: You cannot. Not without re-writing Mike's TPNGGraphic to support the alpha channel.