Search code examples
delphiwinapipngdelphi-xeimage-scaling

Draw Transparent Scaled PNG


I have a large (300x300px) PNG image which I need to rescale. In an other SO question I read about StretchBlt and HALFTONE in order for better scaling.

My problem is how do I draw the PNG image transparent, or at least paint the black corners white?

enter image description here

As you can see on the attached image I get black corners.

And here is the original image: enter image description here Here is what I've tried so far.

unit MainU;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls;

type
  TFormMain = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FormMain: TFormMain;

implementation

{$R *.dfm}
uses
  Pngimage;

type
  TIRButton = class(TImage)
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
  end;

  TGraphicControlAcess = class(TGraphicControl);
  TGraphicAcess = class(TGraphic);

  THalfTonePngImage = class(TPngImage)
  protected
    procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
  end;

  { TIRButton }

constructor TIRButton.Create(AOwner: TComponent);
begin
  inherited;
  Center := True;
  Proportional := True;
end;

procedure TIRButton.Paint;
var
  ParentCanvas: TCanvas;
begin
  ParentCanvas := TGraphicControlAcess(Self).Canvas;
  TGraphicAcess(Picture.Graphic).Draw(ParentCanvas, DestRect);
end;

{ THalfTonePngImage }

procedure THalfTonePngImage.Draw(ACanvas: TCanvas; const Rect: TRect);
var
  p: TPoint;
  dc: HDC;
begin
  dc := ACanvas.Handle;
  GetBrushOrgEx(dc, p);
  SetStretchBltMode(dc, HALFTONE);
  SetBrushOrgEx(dc, p.X, p.Y, @p);

  ACanvas.Brush.Color := clWhite;
  ACanvas.FillRect(Classes.Rect(0, 0, Width, Height));

  StretchBlt(
    dc, 0, Rect.Top, Rect.Right - Rect.Left, Rect.Bottom - Rect.Top,
    Canvas.Handle, 0, 0, Width, Height, ACanvas.CopyMode
    );
end;

procedure TFormMain.FormCreate(Sender: TObject);
var
  Image: THalfTonePngImage;
begin
  Image := THalfTonePngImage.Create;
  Image.LoadFromFile('X200IR_11_EmgBrake.png');

  with TIRButton.Create(Self) do
  begin
    Width := 100;
    Height := 100;
    Picture.Assign(Image);
    Parent := Self;
    Anchors := [akLeft, akTop, akRight, akBottom];
  end;

  Image.Free;
end;

end.

Solution

  • The corners of the source image are transparent. It's not visible in the correct image because the white background. StretchBlt doesn't support transparency. Since it doesn't, transparent pixels are indistinguishable from black. (If the ARGB color is FF 00 00 00 and we remove the alpha channel, we're left with 00 00 00)

    You need to use TransparentBlt.

    For reference this is the image when placed on a green background. It makes it easier to see the transparency.

    enter image description here