Ofer Leshem
Ofer Leshem

Reputation: 21

Delphi drawing part of png file on another image

I am using this function to draw a png over a TImage on a specific location:

procedure TForm1.PlacePNG(nam: string; px, py: Integer);
var
  vPic: TPicture;
  vSrc: TGraphic;
begin
  vPic := TPicture.Create;
  try
    vPic.LoadFromFile(Nam);
    vSrc := vPic.Graphic;
    Image1.Canvas.Draw(px, py, vSrc);
  finally
    vPic.Free;
  end;
end;

My question: what is the best way to do this with part of the png file, without losing its transparency?

Upvotes: 2

Views: 622

Answers (1)

Andreas Rejbrand
Andreas Rejbrand

Reputation: 109158

This is an interesting question!

Of course, drawing the entire PNG is trivial:

procedure TForm1.FormCreate(Sender: TObject);
var
  bg, fg: TPngImage;
begin

  bg := TPngImage.Create;
  try
    bg.LoadFromFile('K:\bg.png');
    fg := TPngImage.Create;
    try
      fg.LoadFromFile('K:\fg.png');
      Image1.Picture.Graphic := bg;
      Image2.Picture.Graphic := fg;
      fg.Draw(bg.Canvas, Rect(0, 0, fg.Width, fg.Height));
      Image3.Picture.Graphic := bg;
    finally
      fg.Free;
    end;
  finally
    bg.Free;
  end;

end;

Output

To draw only a part, one possible solution is to obtain the images as 32-bpp RGBA bitmaps and then use the Windows API, specifically, the AlphaBlend function:

procedure TForm1.FormCreate(Sender: TObject);
var
  bg, fg: TPngImage;
  bgbm, fgbm: TBitmap;
  BlendFunction: TBlendFunction;
begin

  // Load background PNG
  bg := TPngImage.Create;
  try

    bg.LoadFromFile('K:\bg.png');

    // Load foreground PNG
    fg := TPngImage.Create;
    try

      fg.LoadFromFile('K:\fg.png');

      // Preview background and foreground
      Image1.Picture.Graphic := bg;
      Image2.Picture.Graphic := fg;

      // Create background BMP
      bgbm := TBitmap.Create;
      try

        bgbm.Assign(bg);

        // Create foreground BMP
        fgbm := TBitmap.Create;
        try

          fgbm.Assign(fg);

          // Blend PART OF foreground BMP onto background BMP
          BlendFunction.BlendOp := AC_SRC_OVER;
          BlendFunction.BlendFlags := 0;
          BlendFunction.SourceConstantAlpha := 255;
          BlendFunction.AlphaFormat := AC_SRC_ALPHA;
          if not Winapi.Windows.AlphaBlend(
            bgbm.Canvas.Handle,
            100,
            100,
            200,
            200,
            fgbm.Canvas.Handle,
            200,
            200,
            200,
            200,
            BlendFunction
          ) then
            RaiseLastOSError;

          // Preview result
          Image3.Picture.Graphic := bgbm;

        finally
          fgbm.Free;
        end;

      finally
        bgbm.Free;
      end;

    finally
      fg.Free;
    end;

  finally
    bg.Free;
  end;

end;

Output

Upvotes: 5

Related Questions