トップへ(mam-mam.net/)

Improving Image Scaling Quality in Delphi with StretchBlt and SetStretchBltMode (HALFTONE)

Japanese

Improving Image Scaling Quality in Delphi with StretchBlt and SetStretchBltMode (HALFTONE)

When scaling images in Delphi, the TCanvas.StretchDraw method may cause thin lines or small dots to disappear. This happens because the default COLORONCOLOR stretch mode often produces noticeable quality degradation.
This page explains how to achieve smoother and higher‑quality image scaling by using the Windows API functions StretchBlt and SetStretchBltMode with the HALFTONE mode enabled.
The examples include proper handling of HDCs, raster operation flags, and other details required for stable and high‑quality image processing in Delphi.

By setting the stretch mode to HALFTONE using SetStretchBltMode and performing the copy with StretchBlt, you can often achieve noticeably better scaling results compared to the default behavior.

SetStretchBltMode API Function

function SetStretchBltMode(DC: HDC; StretchMode: Integer): Integer; stdcall;

HDC
Specifies the handle to the device context.
(Example) Canvas.Handle
StretchMode
Specifies the stretch mode to use when scaling the image.
BLACKONWHITE
or
STRETCH_ANDSCANS
Sets each destination pixel to the result of a bitwise AND operation between the corresponding source and destination pixels.
COLORONCOLOR
or
STRETCH_DELETESCANS
Replaces each destination pixel with the corresponding source pixel. This is the default mode and may cause visible quality loss when shrinking images.
HALFTONE
or
STRETCH_HALFTONE
Replaces each destination pixel with a color value computed from the average of the surrounding source pixels. This mode provides the highest quality when reducing image size.
WHITEONBLACK
or
STRETCH_ORSCANS
Sets each destination pixel to the result of a bitwise OR operation between the corresponding source and destination pixels.
Return Value
If the function succeeds, it returns the previous stretch mode.
If it fails, the return value is 0.

StretchBlt API Function

function StretchBlt(
  DestDC: HDC;
  XDest, YDest, DestWidth, DestHeight: Integer;
  SrcDC: HDC;
  XSrc, YSrc, SrcWidth, SrcHeight: Integer;
  Rop: DWORD
): BOOL; stdcall;

HDC
Specifies the handle to the destination device context.
XDest
Specifies the X‑coordinate of the upper‑left corner of the destination rectangle.
YDest
Specifies the Y‑coordinate of the upper‑left corner of the destination rectangle.
DestWidth
Specifies the width of the destination rectangle.
DestHeight
Specifies the height of the destination rectangle.
HDC
Specifies the handle to the source device context.
XSrc
Specifies the X‑coordinate of the upper‑left corner of the source rectangle.
YSrc
Specifies the Y‑coordinate of the upper‑left corner of the source rectangle.
SrcWidth
Specifies the width of the source rectangle.
SrcHeight
Specifies the height of the source rectangle.
Rop
Specifies the raster‑operation code (ROP) used during the copy. Common values include:
BLACKNESS Fills the destination area with black.
DSTINVERT Inverts the destination pixels.
NOTSRCERASE Sets each destination pixel to the inverse of the bitwise OR between the source and destination pixels.
SRCAND Sets each destination pixel to the bitwise AND of the source and destination pixels.
SRCCOPY Copies the source pixels directly to the destination.
SRCERASE Sets each destination pixel to the inverse of the bitwise AND between the source and destination pixels.
SRCINVERT Sets each destination pixel to the bitwise XOR of the source and destination pixels.
SRCPAINT Sets each destination pixel to the bitwise OR of the source and destination pixels.
WHITENESS Fills the destination area with white.

Try It Out

Designing the Form and Adding the Source Code

Start the Delphi IDE and create a new project by selecting [File] → [New] → [Windows VCL Application – Delphi].
Place two TImage components and two TButton components on the form.

Copy and paste the following source code into your project.

Assign FormCreate to the TForm1.OnCreate event.
Assign Button1Click to the Button1.OnClick event.
Assign Button2Click to the Button2.OnClick event.

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Image2: TImage;
    Button1: TButton;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var src,dest:TBitmap;
begin
  src := Image1.Picture.Bitmap;
  dest := Image2.Picture.Bitmap;
  dest.Canvas.CopyMode := cmSrcCopy;
  dest.Canvas.StretchDraw(
    Rect(
      0, 0, dest.Width, dest.Height
    ),
    src
  );
end;

procedure TForm1.Button2Click(Sender: TObject);
var src,dest:TBitmap;
begin
  src := Image1.Picture.Bitmap;
  dest := Image2.Picture.Bitmap;
  SetStretchBltMode(dest.Canvas.Handle, HALFTONE);
  StretchBlt(
    dest.Canvas.Handle,
    0, 0, dest.Width, dest.Height,
    src.Canvas.Handle,
    0, 0, src.Width, src.Height,
    SRCCOPY
  );
  Image2.Invalidate;
end;

procedure TForm1.FormCreate(Sender: TObject);
var c:TCanvas;
    w,h:Integer;
    x,y:Integer;
begin
  w := 200;
  h := 200;
  Image1.Width := w;
  Image1.Height := h;
  Image1.Picture.Bitmap.SetSize(w,h);

  Image2.Width  := w div 2;
  Image2.Height := h div 2;
  Image2.Picture.Bitmap.SetSize(w div 2, h div 2);

  c := Image1.Picture.Bitmap.Canvas;
  c.Brush.Color := $F8FFF8;
  c.FillRect(Rect(0,0,w,h));

  // Draw vertical red lines
  c.Pen.Width := 1;
  c.Pen.Color := clRed;
  x := 20;
  while x < w do
  begin
    c.MoveTo(x, 0);
    c.LineTo(x, h);
    Inc(x, 32);
  end;

  // Draw horizontal blue lines
  c.Pen.Width := 2;
  c.Pen.Color := clBlue;
  y := 20;
  while y < h do
  begin
    c.MoveTo(0, y);
    c.LineTo(w, y);
    Inc(y, 32);
  end;
end;

end.

Running the Example

Run the application.
When you click Button1, the image in Image1 is scaled down to half size and displayed in Image2.
Since this uses TCanvas.StretchDraw, the result may look blurry or lose detail.


When you click Button2, the image is again scaled to half size and drawn into Image2.
Because this version uses SetStretchBltMode together with StretchBlt, the result is noticeably smoother and clearer.