Question

How to merge two PNG's together? I know that you can't use PNGObject.Draw because it doesn't copy alpha transaperncy (I am not sure but it doesn't work anyway) so custom procedure/function is needed. I didn't come with empty hands, I have this procedure:

procedure MergePNGLayer(Layer1,Layer2: TPNGObject; Const aLeft,aTop:Integer);
var
  x, y: Integer;
  SL1,  SL2,  SLBlended : pRGBLine;
  aSL1, aSL2, aSLBlended: PByteArray;
  blendCoeff: single;
  blendedPNG, Lay2buff: TPNGObject;
begin
  blendedPNG:=TPNGObject.Create;
  blendedPNG.Assign(Layer1);
  Lay2buff:=TPNGObject.Create;
  Lay2buff.Assign(Layer2);
  SetPNGCanvasSize(Layer2,Layer1.Width,Layer1.Height,aLeft,aTop);
  for y := 0 to Layer1.Height-1 do
  begin
    SL1 := Layer1.Scanline[y];
    SL2 := Layer2.Scanline[y];
    aSL1 := Layer1.AlphaScanline[y];
    aSL2 := Layer2.AlphaScanline[y];
    SLBlended := blendedPNG.Scanline[y];
    aSLBlended := blendedPNG.AlphaScanline[y];
    for x := 0 to Layer1.Width-1 do
    begin
      blendCoeff:=aSL1[x] * 100/255/100;
      aSLBlended[x] := round(aSL2[x] + (aSL1[x]-aSL2[x]) * blendCoeff);
      SLBlended[x].rgbtRed   := round(SL2[x].rgbtRed + (SL1[x].rgbtRed-SL2[x].rgbtRed) * blendCoeff);
      SLBlended[x].rgbtGreen := round(SL2[x].rgbtGreen + (SL1[x].rgbtGreen-SL2[x].rgbtGreen) * blendCoeff);
      SLBlended[x].rgbtBlue  := round(SL2[x].rgbtBlue + (SL1[x].rgbtBlue-SL2[x].rgbtBlue) * blendCoeff);
    end;
  end;
Layer1.Assign(blendedPNG);
Layer2.Assign(Lay2buff);
blendedPNG.Free;
Lay2buff.Free;
end;

But sadly it doesn't work how it should, it does the job but not right. When it merges empty image with loaded image, it works fine, but when both images aren't empty, it makes them loss transparancy.

procedure TForm1.FormClick(Sender: TObject);
var
PNG1, PNG2, PNG3, Dest: TPNGObject;
begin
PNG1 := TPNGObject.Create;
PNG2 := TPNGObject.Create;
PNG3 := TPNGObject.Create;

PNG1.LoadFromFile('Aero\TopLeft.png');//Width 10
PNG2.LoadFromFile('Aero\Top.png');//Width 200
PNG3.LoadFromFile('Aero\TopRight.png');//Width 10

Dest := TPNGObject.CreateBlank(COLOR_RGBALPHA, 16, 220, 10);
MergePNGLayer(Dest, PNG1, 0, 0);
MergePNGLayer(Dest, PNG2, 10, 0);
MergePNGLayer(Dest, PNG3, 210, 0);
Dest.SaveToFile('C:\OUT.PNG');
end;

Wanted result:

enter image description here

Actual result:

enter image description here

I am not sure if you can see differences between these imgaes, but you should open these in PNG editor software and you will see the difference. So I need other procedure to merge PNGs. I am using newest version of PNGImage by the way.

Thanks and have a good day!

Was it helpful?

Solution

This seems to work just fine:

procedure DrawPngWithAlpha(Src, Dest: TPNGObject; const R: TRect);
var
  X, Y: Integer;
  Alpha: PByte;
begin
  Src.Draw(Dest.Canvas, R);

  // I have no idea why standard implementation of TPNGObject.Draw doesn't apply transparency.
  for Y := R.Top to R.Bottom - 1 do
    for X := R.Left to R.Right - 1 do
    begin
      Alpha := @Dest.AlphaScanline[Y]^[X];
      Alpha^ := Min(255, Alpha^ + Src.AlphaScanline[Y - R.Top]^[X - R.Left]);
    end;
end;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top