DelphiでStretchBlt APIを使った画像縮小コピー|SetStretchBltModeで画質改善
Delphiで画像を縮小コピーする際、TCanvasのStretchDrawメソッドでは線や点が消えることがあります。
これはCOLORONCOLORモードによる画質劣化が原因です。
本ページでは、Windows APIのStretchBlt関数とSetStretchBltModeを使ってHALFTONEモードに設定し、より滑らかな画像縮小を実現する方法をソースコード付きで解説します。
HDCの扱いやラスタオペレーションの指定方法も含め、Delphiでの画像処理を安定かつ高品質に行うための実装例です。
SetStretchBltMode APIで HALFTONE(ハーフトーン)モードに設定し、
StretchBlt APIを使用して画像をコピーすると少し綺麗に画像転送できる可能性があります。
SetStretchBltMode API関数
function SetStretchBltMode(DC: HDC; StretchMode: Integer): Integer; stdcall;
- HDC
-
デバイスコンテキストのハンドルを指定します。
(例) Canvas.Handle - StretchMode
-
ストレッチモードを指定します。
BLACKONWHITE
又は
STRETCH_ANDSCANSコピー先のピクセル と コピー元のピクセル をAND演算して コピー先のピクセル に設定します。 COLORONCOLOR
又は
STRETCH_DELETESCANSコピー先のピクセル を コピー元のピクセル で置き換えます。 HALFTONE
又は
STRETCH_HALFTONEコピー先のピクセル を コピー元のピクセル の平均値色で置き換えます。 WHITEONBLACK
又は
STRETCH_ORSCANSコピー先のピクセル と コピー元のピクセル をOR演算して コピー先のピクセル に設定します。 - 戻り値
-
関数が成功した場合は設定する前のストレッチ モード、
失敗した場合は 0 を返す。
StretchBlt API関数
function StretchBlt(
DestDC: HDC;
XDest, YDest, DestWidth, DestHeight: Integer;
SrcDC: HDC;
XSrc, YSrc, SrcWidth, SrcHeight: Integer;
Rop: DWORD
): BOOL; stdcall;
- HDC
- コピー先デバイスコンテキストのハンドルを指定します
- XDest
- コピー先 の左上のX座標を指定します
- YDest
- コピー先 の左上のY座標を指定します
- DestWidth
- コピー先 の幅を指定します
- DestHeight
- コピー先 の高さを指定します
- HDC
- コピー元デバイスコンテキストのハンドルを指定します
- XSrc
- コピー元 の左上のX座標を指定します
- YSrc
- コピー元 の左上のY座標を指定します
- SrcWidth
- コピー元 の幅を指定します
- SrcHeight
- コピー元 の高さを指定します
- Rop
-
ラスタオペレーションを指定します。
指定できる主な値は以下BLACKNESS コピー先 のピクセルを黒色に塗ります。 DSTINVERT コピー先 のピクセルを反転します。 NOTSRCERASE コピー先のピクセル と コピー元のピクセル をOR演算して 反転してコピー先のピクセル に設定します。 SRCAND コピー先のピクセル と コピー元のピクセル をAND演算して コピー先のピクセル に設定します。 SRCCOPY コピー元のピクセル を コピー先のピクセル に設定します。 SRCERASE コピー先のピクセル と コピー元のピクセル をAND演算して 反転してコピー先のピクセル に設定します。 SRCINVERT コピー先のピクセル と コピー元のピクセル をXOR演算して コピー先のピクセル に設定します。 SRCPAINT コピー先のピクセル と コピー元のピクセル をOR演算して コピー先のピクセル に設定します。 WHITENESS コピー先 のピクセルを白色に塗ります。
試してみる
画面設計とソースコードの記述
Delphi IDEを起動し、[ファイル]⇒[新規作成]⇒[Windows VCL アプリケーション -Delphi]をクリックして新規プロジェクトを作成します。
フォームに、TImage×2個 と TButton×2個 をドラッグ&ドロップします。
以下ソースコードをコピー&ペーストします。
TForm1.OnCreate イベントプロパティに FormCreate を設定します。
Button1.OnClick イベントプロパティに Button1Click を設定します。
Button2.OnClick イベントプロパティに Button2Click を設定します。
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 宣言 }
public
{ Public 宣言 }
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));
//縦方向に赤い線を引く
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;
//横方向に青い線を引く
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.
実行する
実行します。
Button1をクリックすると、Image1 の画像を縦横半分のサイズに縮小して Image2 に表示しますが
StretchDrawメソッドを使っているので画像が綺麗に縮小されているように見えません。
Button2をクリックすると、Image1 の画像を縦横半分のサイズに縮小して Image2 に表示します。
SetStretchBltModeとStretchBlt
を使っているのでそこそこ綺麗に縮小されています。
