DelphiでRegionを使ったクリッピングとウィンドウ形状の制御方法|CreateRectRgn・CreateEllipticRgn活用ガイド
Delphiで画像の一部を切り抜いたり、ウィンドウの形状を自由に変更したい場合、Windows APIのRegion機能が非常に便利です。
本記事では、`CreateRectRgn`、`CreateEllipticRgn`、`CreatePolygonRgn`、`CreateRoundRectRgn`などの関数を使って、矩形・楕円・多角形の領域を作成し、それをクリッピングやウィンドウ形状の制御に活用する方法を詳しく解説します。
特に以下のような用途に役立ちます:
- 指定領域だけを別画像にコピーするクリッピング処理
- 楕円や多角形のウィンドウを作成するアニメーション効果
- 複数のRegionを合成して複雑な形状を作るUI演出
サンプルコードとともに、Delphiでの実装方法を丁寧に紹介していきます。
リージョンを作成するAPI関数
リージョンを作成するAPI関数には以下のようなものがあります。
- CreateEllipticRgn(x1,y1, x2,y2:Integer):HRGN;
-
左上座標(x1,y1) 右下座標(x2,y2)の長方形領域に内接する楕円のリージョンを作成します
var Rgn:HRGN; begin Rgn:=CreateEllipticRgn(10,10,90,90); //リージョンの破棄 DeleteObject(Rgn); end; - CreatePolygonRgn(Points:Array of TPoint, Count:Integer, FillMode:Integer):HRGN;
-
Pointsで指定した頂点の多角形リージョンを作成します。
CountはPointsの数(頂点の数)を指定します。
FillModeは以下の何れかの値を指定します。値 説明 ALTERNATE 偶数-奇数ルールに基づいてポリゴンを塗りつぶす
ポリゴンの任意の点から無限遠に線を引き、その線がポリゴンの辺と奇数回交差する場合、その点は内部と見なすWINDING 非ゼロルールに基づいてポリゴンを塗りつぶす
ポリゴンの任意の点から無限遠に線を引き、その線がポリゴンの辺と交差した回数がゼロ以外の場合、その点は内部と見なすvar Rgn:HRGN; Points: array[0..4] of TPoint; begin Points[0] := Point(90, 10); Points[1] := Point(10, 100); Points[2] := Point(120, 110); Points[3] := Point(10, 10); Points[4] := Point(60, 140); Rgn:=CreatePolygonRgn(Points,Length(Points),WINDING); //リージョンの破棄 DeleteObject(Rgn); end; - CreateRectRgn(x1,y1, x2,y2:Integer):HRGN;
-
左上座標(x1,y1) 右下座標(x2,y2)の長方形のリージョンを作成します
var Rgn:HRGN; begin Rgn:=CreateRectRgn(10,10,90,90); //リージョンの破棄 DeleteObject(Rgn); end; - CreateRoundRectRgn(x1,y1, x2,y2, w,h:Integer):HRGN;
-
左上座標(x1,y1) 右下座標(x2,y2)の角が丸い(角の楕円の幅w,角の楕円の高さh)四角形のリージョンを作成します
var Rgn:HRGN; begin Rgn:=CreateRoundRectRgn(10,10, 90,90, 10,6); //リージョンの破棄 DeleteObject(Rgn); end;
リージョンを結合する
作成した2つのリージョンを結合して、新たなリージョンを作成することが出来ます。 リージョンを結合するにはCombineRgn関数を使います。
- CombineRgn(RgnDest, RgnSrc1,RgnSrc2:HRGN, iMode:Integer);
-
リージョン「RgnSrc1」とリージョン「RgnSrc2」を「iMode」モードで結合したリージョン「RgnDest」を生成します
iModeは以下の何れかの値を指定します。値 説明 RGN_AND 2つの結合領域の論理積(AND)を作成します。 RGN_COPY RgnSrc1をコピーします(RgnSrc2は無視) RGN_DIFF RGNSrc2の一部ではないRgnSrc1の部分を結合します RGN_OR 2つの結合領域の論理和(OR)を作成します。 RGN_XOR 2つの結合領域の排他論理和(XOR)を作成します。
var Rgn1,Rgn2: HRGN; begin Rgn1:=CreateRoundRectRgn(10,10, 90,90, 10,6); Rgn2:=CreateEllipticRgn(50,50,140,140); //Rgn1とRgn2をOR結合してRgn1に入れる CombineRgn(Rgn1,Rgn1,Rgn2,RGN_OR); //リージョンの破棄 DeleteObject(Rgn1); DeleteObject(Rgn2); end;
テスト用のアプリケーションの作成
テスト用のプロジェクトの作成と画面設計
[ファイル]⇒[新規作成]⇒[VCL フォーム アプリケーション -Delphi]をクリックし、新規プロジェクトを作成します。
フォーム(Form1)に右下のペイン[ツールパレット]から「TImage」を2つ、「TButton」を2つドラッグ&ドロップします。
楕円のリージョンを作成して楕円領域をクリッピングして画像をコピー
以下ソースコードをコピーして貼り付けます。
「Button1」のイベントプロパティ「Onclick」に「Button1Click」を設定し、
「Form1」のイベントプロパティ「OnCreate」に「FormCreate」を設定します。
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 Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
Bmp1,Bmp2:TBitmap;
Rgn1: HRGN;
begin
Bmp1:=Image1.Picture.Bitmap;
Bmp2:=Image2.Picture.Bitmap;
//楕円のリージョンを作成 (x1,y1, x2,y2)
Rgn1:=CreateEllipticRgn(30, 50, 170, 150);
//クリッピング領域としてリージョンを設定する
SelectClipRgn(Bmp2.Canvas.Handle, Rgn1);
//Image1.Picture.BitmapをImage2.Picture.Bitmapに描画
Bmp2.Canvas.Draw(0,0,Bmp1);
//リージョンの破棄
DeleteObject(Rgn1);
//クリッピング領域を解除する
SelectClipRgn(Bmp2.Canvas.Handle, 0);
end;
procedure TForm1.FormCreate(Sender: TObject);
var Bmp1,Bmp2:TBitmap;
begin
Image1.Width:=200;
Image1.Height:=200;
Bmp1:=Image1.Picture.Bitmap;
Bmp1.SetSize(Image1.Width,Image1.Height);
Image2.Width := Image1.Width;
Image2.Height := Image1.Height;
Bmp2:=Image2.Picture.Bitmap;
Bmp2.Width:=Image1.Width;
Bmp2.Height:=Image1.Height;
//Image1.Picture.Bitmapに適当に描画
Bmp1.Canvas.Brush.Color:=clWhite;
Bmp1.Canvas.FillRect(bmp1.Canvas.ClipRect);
Bmp1.Canvas.Brush.Color:=clGreen;
Bmp1.Canvas.FillRect(Rect(50,50,150,150));
Bmp1.Canvas.Pen.Width:=4;
Bmp1.Canvas.Pen.Color:=clBlue;
Bmp1.Canvas.MoveTo(0,0);
Bmp1.Canvas.LineTo(Bmp1.Width,Bmp2.Height);
Bmp1.Canvas.Pen.Color:=clYellow;
Bmp1.Canvas.MoveTo(Bmp1.Width,0);
Bmp1.Canvas.LineTo(0,Bmp2.Height);
//Image2.Picture.Bitmapは赤色を塗りつぶす
Bmp2.Canvas.Brush.Color:=clRed;
Bmp2.Canvas.FillRect(bmp2.Canvas.ClipRect);
end;
[実行]⇒[実行]をクリックして実行します。
「Button1」をクリックするとImage1の楕円形(30, 50, 170, 150)領域だけがImage2にコピーされていることが確認できます。
ウィンドウの形をリージョンの形に設定する
ウィンドウの形(ウィンドウ領域)をリージョンの形に設定するには SetWindowRgn API関数を使います。
- SetWindowRgn(hWnd:HWND; hRgn:HRGN; bRedraw:LongBool):HRGN;
-
ウィンドウハンドルがhWndのウィンドウの形(ウィンドウ領域)をhRgnに設定します。
bRedraw が True の場合はウィンドウを再描画します。
var Rgn:HRGN; begin Rgn:=CreateRoundRectRgn(10,10, 900,900, 20,30); //ウィンドウの形をリージョンの形にする SetWindowRgn(Self.Handle, Rgn1, True); //リージョンの破棄 DeleteObject(Rgn); end;
Button2をダブルクリックして以下ソースコードを記述します。
procedure TForm1.Button2Click(Sender: TObject); var Points: array[0..3] of TPoint; Rgn1,Rgn2: HRGN; begin //ポリゴン配列 Points[0] := Point(0, 0); Points[1] := Point(Self.Width, 0); Points[2] := Point(Self.Width, Self.Height div 3 * 2); Points[3] := Point(0, Self.Height div 2); //ポリゴンのリージョンの作成 Rgn1:=CreatePolygonRgn(Points,Length(Points),WINDING); //長方形のリージョンの作成 //Rgn2:=CreateRectRgn(100, 100, 180, 180); //楕円のリージョンの場合 Rgn2:=CreateEllipticRgn(0, Self.Height div 2, Self.Width div 2, Self.Height); //Rgn1とRgn2をOR結合してRgn1に入れる CombineRgn(Rgn1,Rgn1,Rgn2,RGN_OR); //ウィンドウの形をリージョンの形にする SetWindowRgn(Self.Handle, Rgn1, True); //リージョンの破棄 DeleteObject(Rgn1); DeleteObject(Rgn2); end;
[実行]⇒[実行]をクリックして実行します。
「Button2」をクリックするウィンドウの形が変形します。
ウィンドウ領域の解除は以下です。
SetWindowRgn(Self.Handle, 0, True);
アプリケーション起動時のウィンドウをリージョンでアニメーション表示する
TForm1のイベントプロパティ「OnActivate」に以下のソースコードを記述するとウィンドウがアニメーション表示されます。
procedure TForm1.FormActivate(Sender: TObject);
var RectRgn: HRGN;
i:Integer;
begin
for i := 0 to 50 do
begin
//指定した範囲で矩形リージョンを作成
RectRgn := CreateRectRgn(
Width div 2 - Width * i div 100,
Height div 2 - Height * i div 100,
Width div 2 + Width * i div 100,
Height div 2 + Height * i div 100
);
//フォームのウィンドウリージョンを設定
SetWindowRgn(Handle, RectRgn, True);
//作成したリージョンオブジェクトを解放
DeleteObject(RectRgn);
//10ミリ秒待機してアニメーションのスムーズさを調整
Sleep(10);
//アプリケーションのメッセージを処理しUIを更新
Application.ProcessMessages;
end;
//リージョンをリセットして、フォームの形状を元に戻す
SetWindowRgn(Handle, 0, True);
end;
