|
画像・図形の移動リサイズ関係サンプル
R060_楕円を描画するクラスの例 |
動作確認等 |
Windows 10 ビルド 19045 Pro 64 bit + Delphi XE5(UP2) Pro VCL-32 |
 |
R060_ResizeEllipse.zip [8.9 MB] 2025年04月10日版 (EXE 同梱)
描画用クラスのソースコードは [概要] ページからダウンロードする |
楕円描画用のクラスとそのテストプログラムです.
楕円の描画を VCL で行う場合と GDI+ で行う場合,拡張描画領域を使用しない場合と拡張描画領域を行う吐合のクラスを作成してテストしています.
今回テスト用に作成した楕円描画用のクラスに実装しているプロパティは以下だけです. 本ページのテストプログラムは,全て,線の幅の変更と,描画している図形をビットマップ画像として保存するだけとなつています.楕円は,上下左右が反転しても図形は変化しません.そこで,確認用の直線を描画するようにしています. |
プロパティ |
型 (VCL) |
型 (GDI+) |
説明 |
PenWidth |
Integer |
Single |
線の幅. |
PenColor |
TColor |
TGPColor |
線の色 |
01_楕円描画用クラス ( VCL 使用・拡張描画なし ) |
図形の描画に VCL を使用して,拡張描画領域を使用しない楕円描画用クラスのテストプログラムです.コンテナコントロールには TPanel を使用しています. |
 |
図1
拡張描画領域を使用しない場合
|
リスト1
図形の描画に VCL を使用して,拡張描画領域を使用しない楕円描画用クラスののテスト |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls, GDIPOBJ, GDIPAPI, GDIPUTIL,
plResizeEllipse ;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Panel2: TPanel;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
FResizeEllipse : TplResizeEllipse;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
Panel1.Caption := '';
end;
//=============================================================================
// [生成]ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(FResizeEllipse) then begin
FreeAndNil(FResizeEllipse);
end;
FResizeEllipse := TplResizeEllipse.Create(Self);
FResizeEllipse.Parent := Panel2;
FResizeEllipse.InverseDisp := True;
end;
//=============================================================================
// [画像保存] ボタン
// 描画している図形をビットマップ画像として保存
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LGPBitmap : TGPBitmap;
LBitmap : TBitmap;
LImgGUID : TGUID;
begin
if not Assigned(FResizeEllipse) then Exit;
// GetGPBitmap メソッドを使用
LGPBitmap := FResizeEllipse.GetGPBitmap;
if GetEncoderClsid('image/bmp', LImgGUID) < 0 then Exit;
LGPBitmap.Save('01_OriginBitmap.bmp', LImgGUID);
// TGPBitmap を TBitmap に変換して保存
LBitmap := TBitmap.Create;
try
FResizeEllipse.GPBitmapToBitmap(LGPBitmap, LBitmap);
LBitmap.SaveToFile('02_TBitmap.bmp');
finally
FreeAndNil(LBitmap);
end;
end;
//=============================================================================
// [色変更] ボタン
// 描画に使用する線の色を変更
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
if not Assigned(FResizeEllipse) then Exit;
FResizeEllipse.PenColor := clRed;
end;
end.
テストに使用した楕円描画用クラスのソースコードです. |
リスト2
図形の描画に VCL を使用して,拡張描画領域を使用しない楕円描画用クラスのソースコード
このユニットファイルをライブラリのパスが通った場所に置いて使用する |
unit plResizeEllipse;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, plBaseDragResize,
GDIPAPI, GDIPOBJ, GDIPUTIL;
type
TplResizeEllipse = class(TplBaseDragResize)
private
FPenColor : TColor;
FPenWidth : Integer;
FBrushColor : TColor;
procedure SetFPenColor(const Value: TColor);
procedure SetFPenWidth(const Value: Integer);
public
constructor Create(AOwner: TComponent); override;
procedure DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap = nil;
AOffsetX: Integer = 0; AOffsetY: Integer = 0); override;
published
property PenColor : TColor read FPenColor write SetFPenColor;
property PenWidth : Integer read FPenWidth write SetFPenWidth;
end;
implementation
uses
Types,
Math;
{ TplResizeEllipse }
//=============================================================================
// コンストラクタ
//=============================================================================
constructor TplResizeEllipse.Create(AOwner: TComponent);
begin
inherited;
FPenColor := clBlue;
FPenWidth := 10;
end;
//-----------------------------------------------------------------------------
// PenColor プロパティ設定用メソッド
//-----------------------------------------------------------------------------
procedure TplResizeEllipse.SetFPenColor(const Value: TColor);
begin
if FPenColor <> Value then begin
FPenColor := Value;
// 描画を更新
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// PenWidth プロパティ設定用メソッド
//-----------------------------------------------------------------------------
procedure TplResizeEllipse.SetFPenWidth(const Value: Integer);
begin
if FPenWidth <> Value then begin
FPenWidth := Value;
// 描画を更新
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// 実際の描画処理を実行するDrawRoutineメソッド
// 引数のACanvasにこのメソッドの描画コードを実行する
// TGPBitmap として取得する場合は
// 引数の AGPBitmap を描画先とした TGPGraphics を生成して描画する
//
// 描画開始位置は(DrawLeft, DrawTop)
// 幅と高さは DrawWidth, DrawHeight
//-----------------------------------------------------------------------------
procedure TplResizeEllipse.DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap;
AOffsetX: Integer; AOffsetY: Integer);
var
LBitmap : TBitmap;
LTransColor : Cardinal;
LPosX : Integer;
LPosY : Integer;
LPosD : Integer;
LHalfW : Integer;
LHalfH : Integer;
begin
inherited;
if (ACanvas = nil) and (AGPBitmap = nil) then Exit;
// 描画の結果を画像として取得する際の処理
// 以下のコードを実行する前にすでに図形は描画されているので
// AddExtend の値は既知
// LTransColor は TBitmap の透過色
LBitmap := nil;
LTransColor := rgb($57, $57, $57);
//-------------------
// 描画結果を AGPBitmap に返す場合
if ACanvas = nil then begin
// TBitmap を生成してその TCanvas に描画
// 描画後に TGPBitmap に変換
LBitmap := TBitmap.Create;
SetBitmapCanvas_forDraw(LBitmap, ACanvas, LTransColor);
end;
//-------------------
ACanvas.Pen.Width := FPenWidth;
ACanvas.Pen.Color := FPenColor;
ACanvas.Brush.Style := bsClear;
LHalfW := SignX * FPenWidth div 2;
LHalfH := SignY * FPenWidth div 2;
Acanvas.Ellipse(DrawLeft + LHalfw, DrawTop + LHalfH,
DrawLeft + DrawWidth - LHalfW, DrawTop + DrawHeight - LHalfH);
// 参考に直線を追加描画
// 具体的な数値を指定した場合、拡大、縮小してもその値のまま
LPosD := Width div 4;
LPosX := DrawLeft + SignX * LPosD;
LPosY := DrawTop + SignY * 50;
ACanvas.MoveTo(LPosX, LPosY);
LPosY := DrawTop + DrawHeight - LHalfH;
ACanvas.LineTo(LPosX, LPosY);
//-------------------
// 描画結果を AGPBitmap に返す場合
// 描画を AGPBitmap に返す処理
if Assigned(LBitmap) then begin
BitmapToGPBitmap_forDraw(LBitmap, AGPBitmap, LTransColor, AOffsetX, AOffsetY);
FreeAndNil(LBitmap);
end;
end;
end.
上の DrawRoutine メソッドでは,線の幅を考慮して描画位置を設定しています.それ結果,下図のように線の幅に関係なくラバーバンド内に描画されています.
|
 |
図2
線の幅を考慮した描画 |
DrawRoutine メソッド内の変数 LHalfW と LHalfH を 0 にすると線の幅を考慮しない描画となります.その結果,下図のようになります. |
 |
図3
線の幅を考慮しない描画 |
02_楕円描画用クラス ( VCL 使用・拡張描画使用 ) |
図形の描画に VCL を使用して,拡張描画領域を使用する楕円描画用クラスのテストプログラムです.コンテナコントロールには TPanel を使用しています. |
 |
図4
拡張描画領域を使用する場合
|
リスト3
図形の描画に VCL を使用して,拡張描画領域を使用する楕円描画用クラスののテスト |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ExtCtrls,
StdCtrls, GDIPOBJ, GDIPAPI, GDIPUTIL,
plResizeEllipseEx;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Panel2: TPanel;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
FResizeEllipse : TplResizeEllipseEx;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
Panel1.Caption := '';
end;
//=============================================================================
// [生成]ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(FResizeEllipse) then begin
FreeAndNil(FResizeEllipse);
end;
FResizeEllipse := TplResizeEllipseEx.Create(Self);
FResizeEllipse.Parent := Panel2;
FResizeEllipse.InverseDisp := True;
end;
//=============================================================================
// [画像保存] ボタン
// 描画している図形をビットマップ画像として保存
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LGPBitmap : TGPBitmap;
LBitmap : TBitmap;
LImgGUID : TGUID;
begin
if not Assigned(FResizeEllipse) then Exit;
// GetGPBitmap メソッドを使用
LGPBitmap := FResizeEllipse.GetGPBitmap;
if GetEncoderClsid('image/bmp', LImgGUID) < 0 then Exit;
LGPBitmap.Save('01_OriginBitmap.bmp', LImgGUID);
// TGPBitmap を TBitmap に変換して保存
LBitmap := TBitmap.Create;
try
FResizeEllipse.GPBitmapToBitmap(LGPBitmap, LBitmap);
LBitmap.SaveToFile('02_TBitmap.bmp');
finally
FreeAndNil(LBitmap);
end;
end;
//=============================================================================
// [色変更] ボタン
// 描画に使用する線の色を変更
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
if not Assigned(FResizeEllipse) then Exit;
FResizeEllipse.PenColor := clRed;
end;
end.
テストに使用した楕円描画用クラスのソースコードです.
拡張描画領域を使用するには,クラスのコンストラクタ (Create メソッドの実装部) で UseExtend プロパティを True にして,AddExtend プロパティで拡張描画領域のサイズを設定します. |
リスト4
図形の描画に VCL を使用して,拡張描画領域を使用する楕円描画用クラスのソースコード
このユニットファイルをライブラリのパスが通った場所に置いて使用する |
unit plResizeEllipseEx;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, plBaseDragResize,
GDIPAPI, GDIPOBJ, GDIPUTIL;
type
TplResizeEllipseEx = class(TplBaseDragResize)
private
FPenColor : TColor;
FPenWidth : Integer;
procedure SetFPenColor(const Value: TColor);
procedure SetFPenWidth(const Value: Integer);
public
constructor Create(AOwner: TComponent); override;
procedure DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap = nil;
AOffsetX: Integer = 0; AOffsetY: Integer = 0); override;
published
property PenColor : TColor read FPenColor write SetFPenColor;
property PenWidth : Integer read FPenWidth write SetFPenWidth;
end;
implementation
uses
Types,
Math;
{ TplResizeEllipseEx }
//=============================================================================
// コンストラクタ
//=============================================================================
constructor TplResizeEllipseEx.Create(AOwner: TComponent);
begin
inherited;
FPenColor := clBlue;
FPenWidth := 10;
// 拡張描画領域を使用する
// 拡張描画領域が固定でよければここで AddExtend の値を設定してもよい
UseExtend := True;
//AddExtend := 40;
end;
//-----------------------------------------------------------------------------
// PenColor プロパティ設定用メソッド
//-----------------------------------------------------------------------------
procedure TplResizeEllipseEx.SetFPenColor(const Value: TColor);
begin
if FPenColor <> Value then begin
FPenColor := Value;
// 描画を更新
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// PenWidth プロパティ設定用メソッド
//-----------------------------------------------------------------------------
procedure TplResizeEllipseEx.SetFPenWidth(const Value: Integer);
begin
if FPenWidth <> Value then begin
FPenWidth := Value;
// 描画を更新
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// 実際の描画処理を実行するDrawRoutineメソッド
// 引数のACanvasにこのメソッドの描画コードを実行する
// TGPBitmap として取得する場合は
// TBitmap に描画した内容を AGPBitmap に変換して返す
//
// 描画開始位置は(DrawLeft, DrawTop)
// 幅と高さは DrawWidth, DrawHeight
//-----------------------------------------------------------------------------
procedure TplResizeEllipseEx.DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap;
AOffsetX: Integer; AOffsetY: Integer);
var
LBitmap : TBitmap;
LTransColor : Cardinal;
LPosX : Integer;
LPosY : Integer;
LPosD : Integer;
begin
inherited;
if (ACanvas = nil) and (AGPBitmap = nil) then Exit;
if not Assigned(ExtendCtrl) then Exit;
// 描画の結果を画像として取得する際の処理
// 以下のコードを実行する前にすでに図形は描画されているので
// AddExtend の値は既知
// LTransColor は TBitmap の透過色
LBitmap := nil;
LTransColor := rgb($57, $57, $57);
// コントロールの追加サイズ分
// 上下左右に拡張する部分
AddExtend := FPenWidth * 2;
//-------------------
// 描画結果を AGPBitmap に返す場合
if ACanvas = nil then begin
// TBitmap を生成してその TCanvas に描画
// 描画後に TGPBitmap に変換
LBitmap := TBitmap.Create;
SetBitmapCanvas_forDraw(LBitmap, ACanvas, LTransColor);
end;
//-------------------
ACanvas.Pen.Width := FPenWidth;
ACanvas.Pen.Color := FPenColor;
// 楕円を描画
LPosX := DrawLeft + AddExtend;
LPosY := DrawTop + AddExtend;
Acanvas.Ellipse(LPosX, LPosY, LPosX + DrawWidth, LPosY + DrawHeight);
// 参考に直線を追加描画
// 具体的な数値を指定した場合、拡大、縮小してもその値のまま
LPosD := Width div 4;
LPosX := DrawLeft + AddExtend + SignX * LPosD;
LPosY := DrawTop + AddExtend + SignY * 50;
ACanvas.MoveTo(LPosX, LPosY);
LPosY := + DrawTop + AddExtend + DrawHeight;
ACanvas.LineTo(LPosX, LPosY);
//-------------------
// 描画を AGPBitmap に返す処理
if Assigned(LBitmap) then begin
BitmapToGPBitmap_forDraw(LBitmap, AGPBitmap, LTransColor, AOffsetX, AOffsetY);
FreeAndNil(LBitmap);
end;
end;
end.
03_楕円描画用クラス ( VCL 使用・拡張描画使用 ) - 専用コンテナ使用 |
前項と同じ楕円描画用クラスのテストですが,コンテナコントロールに専用の TplContainerPanel を使用しています.これをコンテナとして使用すると,複数の描画コントロールを選択状態にできます.複数の描画コトンロールが選択されていると,それらの図形を結合したビットマップの保存となります. |
 |
図5
複数の図形を選択
|
リスト5
図形の描画に VCL を使用して,拡張描画領域を使用する楕円描画用クラスののテスト |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls, GDIPOBJ, GDIPAPI,
GDIPUTIL, plResizeEllipseEx,
plContainerPanel,
plBaseDragResize;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
FBackCtrl : TplContainerPanel;
FCtrl : TplBaseDragResize;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
Panel1.Caption := '';
FBackCtrl := TplContainerPanel.Create(Self);
FBackCtrl.Parent := Self;
FBackCtrl.Align := alClient;
end;
//=============================================================================
// [生成]ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
// 生成亟みの描画用コントロールを削除
// TplContainerPanel を使用する場合は専用のメソッドを使用する
// これを実行しなければいつくでも生成できる
// FBackCtrl.DeleteAllCtrls;
FCtrl := TplResizeEllipseEx.Create(Self);
FCtrl.Parent := FBackCtrl;
FCtrl.InverseDisp := True;
end;
//=============================================================================
// [選択画像保存] ボタン
// TplContainerPanel のメソッドを使用
// ただし、選択状態でないし取得できない
// 複数の図形が選択されている場合は、それらを結合した画像の取得となる
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LGPBitmap : TGPBitmap;
LImgGUID : TGUID;
begin
LGPBitmap := FBackCtrl.GPBitmapFromSelectedCtrls;
try
if GetEncoderClsid('image/bmp', LImgGUID) < 0 then Exit;
LGPBitmap.Save('01_BackMethod.bmp', LImgGUID);
finally
FreeAndNil(LGPBitmap);
end;
end;
//=============================================================================
// [色変更] ボタン
// 描画に使用する線の色を変更 ある
// plContainerPanel を使用する場合の処理例
// 型キャストして描画用クラスのプロパティやメソッドを使用する
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
LCtrl : TplBaseDragResize;
begin
if FBackCtrl.CtrlList.Count = 0 then Exit;
// 最初に生成した楕円 ( Z オーダは最下位 ) の線の色を変更
if FBackCtrl.CtrlList[0] is TplResizeEllipseEx then begin
LCtrl := FBackCtrl.CtrlList[0];
TplResizeEllipseEx(LCtrl).PenColor := clRed;
end;
end;
end.
04_楕円描画用クラス ( GDI+ 使用・拡張描画なし ) |
図形の描画に GDI+ を使用して,拡張描画領域を使用しない楕円描画用クラスのテストプログラムです.コンテナコントロールには TPanel を使用しています. |
 |
図6
拡張描画領域を使用しない場合
|
リスト6
図形の描画に GDI+ を使用して,拡張描画領域を使用しない楕円描画用クラスののテスト |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls, GDIPOBJ, GDIPAPI, GDIPUTIL,
plGPResizeEllipse;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Panel2: TPanel;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
FResizeEllipse : TplGPResizeEllipse;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
Panel1.Caption := '';
end;
//=============================================================================
// [生成]ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(FResizeEllipse) then begin
FreeAndNil(FResizeEllipse);
end;
FResizeEllipse := TplGPResizeEllipse.Create(Self);
FResizeEllipse.Parent := Panel2;
FResizeEllipse.InverseDisp := True;
end;
//=============================================================================
// [画像保存] ボタン
// 描画している図形をビットマップ画像として保存
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LGPBitmap : TGPBitmap;
LBitmap : TBitmap;
LImgGUID : TGUID;
begin
if not Assigned(FResizeEllipse) then Exit;
// GetGPBitmap メソッドを使用
LGPBitmap := FResizeEllipse.GetGPBitmap;
if GetEncoderClsid('image/bmp', LImgGUID) < 0 then Exit;
LGPBitmap.Save('01_OriginBitmap.bmp', LImgGUID);
// TGPBitmap を TBitmap に変換して保存
LBitmap := TBitmap.Create;
try
FResizeEllipse.GPBitmapToBitmap(LGPBitmap, LBitmap);
LBitmap.SaveToFile('02_TBitmap.bmp');
finally
FreeAndNil(LBitmap);
end;
end;
//=============================================================================
// [色変更] ボタン
// 描画に使用する線の色を変更
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
if not Assigned(FResizeEllipse) then Exit;
FResizeEllipse.PenColor := aclRed;
// VCL の色を GDI+ の色に変換する場合は以下
//FResizeEllipse.PenColor := ColorRefToARGB(clRed);
end;
end.
テストに使用した楕円描画用クラスのソースコードです. |
リスト7
図形の描画に GDI+ を使用して,拡張描画領域を使用しない楕円描画用クラスのソースコード
このユニットファイルをライブラリのパスが通った場所に置いて使用する |
unit plGPResizeEllipse;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, plBaseDragResize,
GDIPAPI, GDIPOBJ, GDIPUTIL;
type
TplGPResizeEllipse = class(TplBaseDragResize)
private
FPenColor : TGPColor;
FBrushColor : TGPColor;
FPenWidth : Single;
procedure SetFPenColor(const Value: TGPColor);
procedure SetFPenWidth(const Value: Single);
public
constructor Create(AOwner: TComponent); override;
procedure DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap = nil;
AOffsetX: Integer = 0; AOffsetY: Integer = 0); override;
published
property PenColor : TGPColor read FPenColor write SetFPenColor;
property BrushColor : TGPColor read FBrushColor write FBrushColor;
property PenWidth : Single read FPenWidth write SetFPenWidth;
end;
implementation
uses
Types,
Math;
{ TplGPResizeEllipse }
//=============================================================================
// コンストラクタ
//=============================================================================
constructor TplGPResizeEllipse.Create(AOwner: TComponent);
begin
inherited;
FBrushColor := aclTransparent;
FPenColor := $FF00A0FF;
FPenWidth := 10;
end;
//-----------------------------------------------------------------------------
// PenColor プロパティ設定用メソッド
//-----------------------------------------------------------------------------
procedure TplGPResizeEllipse.SetFPenColor(const Value: TGPColor);
begin
if FPenColor <> Value then begin
FPenColor := Value;
// 描画を更新
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// PenWidth プロパティ設定用メソッド
//-----------------------------------------------------------------------------
procedure TplGPResizeEllipse.SetFPenWidth(const Value: Single);
begin
if FPenWidth <> Value then begin
FPenWidth := Value;
// 描画を更新
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// 実際の描画処理を実行するDrawRoutineメソッド
// 引数のACanvasにこのメソッドの描画コードを実行する
// TGPBitmap として取得する場合は
// 引数の AGPBitmap を描画先とした TGPGraphics を生成して描画する
//
// 描画開始位置は(DrawLeft, DrawTop)
// 幅と高さは DrawWidth, DrawHeight
//-----------------------------------------------------------------------------
procedure TplGPResizeEllipse.DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap;
AOffsetX: Integer; AOffsetY: Integer);
var
LGPGraphic : TGPGraphics;
LGPPen : TGPPen;
LGPBrush : TGPSolidBrush;
LHalfW : Single;
LHalfH : Single;
LGPRect : TGPRectF;
LPosX : Single;
LPosY : Single;
LPosD : Single;
begin
inherited;
if (ACanvas = nil) and (AGPBitmap = nil) then Exit;
if (FPenColor = aclTransparent) and (FBrushColor = aclTransparent) then exit;
// TGPGraphicsオブジェクトを生成
if ACanvas = nil then begin
LGPGraphic := TGPGraphics.Create(AGPBitmap);
end else begin
LGPGraphic := TGPGraphics.Create(ACanvas.Handle);
end;
// ペンのオブジェクトを生成
LGPPen := TGPPen.Create(FPenColor, FPenWidth);
//TGPSolidBrushを生成
LGPBrush := TGPSolidBrush.Create(FBrushColor);
try
LHalfW := SignX * FPenWidth / 2.0;
LHalfH := SignY * FPenWidth / 2.0;
LGPRect := MakeRect(DrawLeft + Lhalfw, DrawTop + LhalfH,
Drawwidth - LHalfW * 2.0, DrawHeight - LHalfH * 2.0);
if FBrushColor <> aclTransparent then begin
// 内部を塗り潰す
LGPGraphic.FillEllipse(LGPBrush, LGPRect);
end;
if FPenColor <> aclTransparent then begin
// 周囲の枠線を描画
// 線の描画関係の関数名は DrawXXXX
LGPGraphic.DrawEllipse(LGPPen, LGPRect);
// 参考に直線を追加描画
// 具体的な数値を指定した場合、拡大、縮小してもその値のまま
LPosD := Width / 4.0;
LPosX := DrawLeft + SignX * LPosD;
LPosY := DrawTop + SignY * 50.0;
LGPGraphic.DrawLine(LGPPen, LPosX, LPosY, LPosX, DrawTop + DrawHeight);
end;
finally
FreeAndNil(LGPBrush);
FreeAndNil(LGPPen);
FreeAndNil(LGPGraphic);
end;
end;
end.
05_楕円描画用クラス ( GDI+ 使用・拡張描画使用 ) |
図形の描画に VCL を使用して,拡張描画領域を使用する楕円描画用クラスのテストプログラムです.コンテナコントロールには TPanel を使用しています. |
 |
図7
拡張描画領域を使用する場合
|
リスト8
図形の描画に GDI+ を使用して,拡張描画領域を使用する楕円描画用クラスののテスト |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls, GDIPOBJ, GDIPAPI, GDIPUTIL,
plGPResizeEllipseEx;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Panel2: TPanel;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
FResizeEllipse : TplGPResizeEllipseEx;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
Panel1.Caption := '';
end;
//=============================================================================
// [生成]ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(FResizeEllipse) then begin
FreeAndNil(FResizeEllipse);
end;
FResizeEllipse := TplGPResizeEllipseEx.Create(Self);
FResizeEllipse.Parent := Panel2;
FResizeEllipse.InverseDisp := True;
end;
//=============================================================================
// [画像保存] ボタン
// 描画している図形をビットマップ画像として保存
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LGPBitmap : TGPBitmap;
LBitmap : TBitmap;
LhBitmap : HBITMAP;
LImgGUID : TGUID;
begin
if not Assigned(FResizeEllipse) then Exit;
// GetGPBitmap メソッドを使用
LGPBitmap := FResizeEllipse.GetGPBitmap;
if GetEncoderClsid('image/bmp', LImgGUID) < 0 then Exit;
LGPBitmap.Save('01_OriginBitmap.bmp', LImgGUID);
// TGPBitmap を TBitmap に変換して保存
LBitmap := TBitmap.Create;
try
LBitmap.AlphaFormat := afDefined;
LGPBitmap.GetHBITMAP(0, LhBitmap);
LBitmap.Handle := LhBitmap;
LBitmap.SaveToFile('02_TBitmap.bmp');
finally
FreeAndNil(LBitmap);
end;
end;
//=============================================================================
// [色変更] ボタン
// 描画に使用する線の色を変更
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
if not Assigned(FResizeEllipse) then Exit;
FResizeEllipse.PenColor := aclRed;
// VCL の色を GDI+ の色に変換する場合は以下
//FResizeEllipse.PenColor := ColorRefToARGB(clRed);
end;
end.
テストに使用した楕円描画用クラスのソースコードです.
拡張描画領域を使用するには,クラスのコンストラクタ (Create メソッドの実装部) で UseExtend プロパティを True にして,AddExtend プロパティで拡張描画領域のサイズを設定します. |
リスト9
図形の描画に GDI+ を使用して,拡張描画領域を使用する楕円描画用クラスのソースコード
このユニットファイルをライブラリのパスが通った場所に置いて使用する |
unit plGPResizeEllipseEx;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, plBaseDragResize,
GDIPAPI, GDIPOBJ, GDIPUTIL;
type
TplGPResizeEllipseEx = class(TplBaseDragResize)
private
FPenColor : TGPColor;
FBrushColor : TGPColor;
FPenWidth : Single;
procedure SetFPenColor(const Value: TGPColor);
procedure SetFPenWidth(const Value: Single);
public
constructor Create(AOwner: TComponent); override;
procedure DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap = nil;
AOffsetX: Integer = 0; AOffsetY: Integer = 0); override;
published
property PenColor : TGPColor read FPenColor write SetFPenColor;
property BrushColor : TGPColor read FBrushColor write FBrushColor;
property PenWidth : Single read FPenWidth write SetFPenWidth;
end;
implementation
uses
Types,
Math;
{ TplGPResizeEllipseEx }
//=============================================================================
// コンストラクタ
//=============================================================================
constructor TplGPResizeEllipseEx.Create(AOwner: TComponent);
begin
inherited;
FBrushColor := aclTransparent;
FPenColor := $FF00A0FF;
FPenWidth := 10;
// 拡張描画領域を使用する
// 拡張描画領域が固定でよければここで AddExtend の値を設定してもよい
UseExtend := True;
//AddExtend := 40;
end;
//-----------------------------------------------------------------------------
// PenColor プロパティ設定用メソッド
//-----------------------------------------------------------------------------
procedure TplGPResizeEllipseEx.SetFPenColor(const Value: TGPColor);
begin
if FPenColor <> Value then begin
FPenColor := Value;
// 描画を更新
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// PenWidth プロパティ設定用メソッド
//-----------------------------------------------------------------------------
procedure TplGPResizeEllipseEx.SetFPenWidth(const Value: Single);
begin
if FPenWidth <> Value then begin
FPenWidth := Value;
// 描画を更新
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// 実際の描画処理を実行するDrawRoutineメソッド
// 引数のACanvasにこのメソッドの描画コードを実行する
// TGPBitmap として取得する場合は
// 一時的な TGPBitmap を描画先とした TGPGraphics を生成して描画
// 最後に引数の AGPBitmap を描画先にして返す
//
// 描画開始位置は(DrawLeft, DrawTop)
// 幅と高さは DrawWidth, DrawHeight
//-----------------------------------------------------------------------------
procedure TplGPResizeEllipseEx.DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap;
AOffsetX: Integer; AOffsetY: Integer);
var
LGPGraphic : TGPGraphics;
LTempGPBmp : TGPBitmap;
LGPPen : TGPPen;
LGPBrush : TGPSolidBrush;
LGPRect : TGPRectF;
LPosX : Single;
LPosY : Single;
LPosD : Single;
begin
inherited;
if (ACanvas = nil) and (AGPBitmap = nil) then Exit;
if not Assigned(ExtendCtrl) then Exit;
if (FPenColor = aclTransparent) and (FBrushColor = aclTransparent) then exit;
// コントロールの追加サイズ分
AddExtend := Trunc(FPenWidth * 2);
//---------------------
// 描画結果を AGPBitmap に返す場合
// 描画領域を拡張した一時的な TGPBitmap に描画する
LTempGPBmp := nil;
if ACanvas = nil then begin
LTempGPBmp := TGPBitmap.Create(Width + AddExtend * 2, Height + AddExtend * 2);
end;
//---------------------
// TGPGraphicsオブジェクトを生成 (引数が描画先)
if ACanvas = nil then begin
LGPGraphic := TGPGraphics.Create(LTempGPBmp);
end else begin
LGPGraphic := TGPGraphics.Create(ACanvas.Handle);
end;
// ペンのオブジェクトを生成
LGPPen := TGPPen.Create(FPenColor, FPenWidth);
//TGPSolidBrushを生成
LGPBrush := TGPSolidBrush.Create(FBrushColor);
try
LPosX := DrawLeft + AddExtend;
LPosY := DrawTop + AddExtend;
LGPRect := MakeRect(LPosX, LPosY, Drawwidth, DrawHeight);
if FBrushColor <> aclTransparent then begin
// 内部を塗り潰す
LGPGraphic.FillEllipse(LGPBrush, LGPRect);
end;
if FPenColor <> aclTransparent then begin
// 周囲の枠線を描画
// 線の描画関係の関数名は DrawXXXX
LGPGraphic.DrawEllipse(LGPPen, LGPRect);
// 参考に直線を追加描画
// 具体的な数値を指定した場合、拡大、縮小してもその値のまま
LPosD := Width / 4.0;
LPosX := DrawLeft + AddExtend + SignX * LPosD;
LPosY := DrawTop + AddExtend + SignY * 50.0;
LGPGraphic.DrawLine(LGPPen, LPosX, LPosY, LPosX, DrawTop + AddExtend + DrawHeight);
end;
//---------------------
// 描画結果を AGPBitmap に返す場合
// 描画先を AGPBitmap にした TGPGraphics に LTempGPBmp を描画
if ACanvas = nil then begin
FreeAndNil(LGPGraphic);
LGPGraphic := TGPGraphics.Create(AGPBitmap);
LGPGraphic.DrawImage(LTempGPBmp, AOffsetX, AOffsetY);
end;
//---------------------
finally
FreeAndNil(LGPBrush);
FreeAndNil(LGPPen);
FreeAndNil(LGPGraphic);
if Assigned(LTempGPBmp) then begin
FreeAndNil(LTempGPBmp);
end;
end;
end;
end.
06_楕円描画用クラス ( GDI+ 使用・拡張描画使用 ) - 専用コンテナ使用 |
前項と同じ楕円描画用クラスのテストですが,コンテナコントロールに専用の TplContainerPanel を使用しています.これをコンテナとして使用すると,複数の描画コントロールを選択状態にできます.複数の描画コトンロールが選択されていると,それらの図形を結合したビットマップの保存となります. |
 |
図8
複数の図形を選択
|
リスト10
図形の描画に GDI+ を使用して,拡張描画領域を使用する楕円描画用クラスのテスト |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls, GDIPOBJ, GDIPAPI, GDIPUTIL,
plGPResizeEllipseEx,
plContainerPanel,
plBaseDragResize;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
FBackCtrl : TplContainerPanel;
FCtrl : TplBaseDragResize;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
//=============================================================================
// フォーム生成時の処理
// 専用のコンテナコントロールを生成
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
Panel1.Caption := '';
FBackCtrl := TplContainerPanel.Create(Self);
FBackCtrl.Parent := Self;
FBackCtrl.Align := alClient;
end;
//=============================================================================
// [生成]ボタン
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
// 生成済みの描画用コントロールを削除
// TplContainerPanel を使用する場合は専用のメソッドを使用する
// これを実行しなければいつくでも生成できる
// FBackCtrl.DeleteAllCtrls;
FCtrl := TplGPResizeEllipseEx.Create(Self);
FCtrl.Parent := FBackCtrl;
FCtrl.InverseDisp := True;
end;
//=============================================================================
// [選択画像保存] ボタン
// TplContainerPanel のメソッドを使用
// ただし、選択状態でないと取得できない
// 複数の図形が選択されている場合は、それらを結合した画像の取得となる
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LGPBitmap : TGPBitmap;
LImgGUID : TGUID;
begin
LGPBitmap := FBackCtrl.GPBitmapFromSelectedCtrls;
try
if GetEncoderClsid('image/bmp', LImgGUID) < 0 then Exit;
LGPBitmap.Save('01_BackMethod.bmp', LImgGUID);
finally
FreeAndNil(LGPBitmap);
end;
end;
//=============================================================================
// [色変更] ボタン
// 描画に使用する線の色を変更 ある
// plContainerPanel を使用する場合の処理例
// 型キャストして描画用クラスのプロパティやメソッドを使用する
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
LCtrl : TplBaseDragResize;
begin
if FBackCtrl.CtrlList.Count = 0 then Exit;
// 最初に生成した楕円 ( Z オーダは最下位 ) の線の色を変更
if FBackCtrl.CtrlList[0] is TplGPResizeEllipseEx then begin
LCtrl := FBackCtrl.CtrlList[0];
TplGPResizeEllipseEx(LCtrl).PenColor := aclRed;
// VCL の色を GDI+ の色に変換する場合は以下
//TplGPResizeEllipseEx(LCtrl).PenColor := ColorRefToARGB(clRed);
end;
end;
end.
本ページのサンプルで使用している描画用クラスでは,縦の直線を描画しています.
この直線の描画支点の Y 座標は,ラバーバンド上端から 50 ドット下の固定値となっています.したがって,ラバーバンドをリサイズしても,この 50 という値は変わりません. |
 |
図9
図形を生成
- 縦の直線の始点の Y 座標は,ラバーバンド
上端から 50
|
 |
図10
リサイズ
- リサイズしても,縦の直線の始点の Y 座標は,ラバーバンド 上端から 50
|
 |
図11
ラバーバンドの高さを 50 以下にする
|
この楕円描画用コトンロールの初期サイズは 100 x 100 です.50 というのはこの半分,つまり 50% ま位置です.縦の直線の描画始点の Y 座標を常に 50% の位置になるようにしてみます.描画コントロールの高さが 200 になったら 100 になるようにします.
まず,描画用コントロールを生成した時の幅と高さを保持しておくグローバル変数を定義して,Create メソッドで値を設定します. |
// Creatae メソッドで以下の変数を設定
// FOriginWidth, FOriginHeight は,Integer 型のグローバル変数
FOriginWidth := Width;
FOriginHeight := Height;
//-----------------------------------------------------------------------------
// X 座標値をリサイズに応じて変換
//-----------------------------------------------------------------------------
function TplGPResizeEllipseEx.ToRelativeX(X: Single): Single;
var
LRatioX : Single;
begin
LRatioX := Width / FOriginWidth;
Result := DrawLeft + AddExtend + SignX * LRatioX * X;
end;
//-----------------------------------------------------------------------------
// Y 座標値をリサイズに応じて変換
//-----------------------------------------------------------------------------
function TplGPResizeEllipseEx.ToRelativeY(Y: Single): Single;
var
LRatioY : Single;
begin
LRatioY := Height / FOriginHeight;
Result := DrawTop + AddExtend + SignY * LRatioY * Y;
end;
DrawRoutine メソッド内の,直線を描画するコードを以下のようにします. |
LPosX := ToRelativeX(25.0);
LPosY := ToRelativeY(50.0);
LGPGraphic.DrawLine(LGPPen, LPosX, LPosY, LPosX, DrawTop + AddExtend + DrawHeight);
下図は実行結果です.
このように,座標値を相対値として処理できます.作成した関数内の LRatioX, LRatioY を 1.0 にすれば,引数の座標値を絶対値として処理できます.
当然ですが,Create メソッドで描画用コントロールまサイズを 150 x 150 (Width = 150, Height = 150) にした場合,50 ドットは 33.33% になります. |
 |
図12
生成してリサイズ
- 縦の直線の描画始点 Y 座標は,描画用コトンロールの高さの半分
|
 |
図13
ラバーバンドの高さを 50 以下にする
- 縦の直線の描画始点 Y 座標は,描画用コトンロールの高さの半分
これは上の図と同じ
|
TGPPointF を使用する場合は,例えば以下のような関数を実装します.LRatioX, LRatioY はグローバル変数にして,DrawRoutine メソッド内で計算した方がいいかも知れません. |
//-----------------------------------------------------------------------------
// TPoint 座標値をリサイズに応じて変換
//-----------------------------------------------------------------------------
function TplGPResizeEllipseEx.ToRelativePos(APos: TGPPointF): TGPPointF;
var
LRatioX : Single;
LRatioY : Single;
begin
LRatioX := Width / FOriginWidth;
LRatioY := Height / FOriginHeight;
Result.X := DrawLeft + AddExtend + SignX * LRatioX * APos.X;
Result.Y := DrawTop + AddExtend + SignY * LRatioY * APOS.Y;
end;
08_リサイズに応じて座標値を変換 - 座標値のリスト |
座標値のリスト TList<TGPointF> を使用する方法です.
直線は端点として始点と終点の 2 組の座標値を使用しますが,汎用的に処理するために次のようなメソッドを実装することにします. |
//-----------------------------------------------------------------------------
// 座標値リストの座標値をリサイズに応じて変換
//
// TList<TGPPointF> は,利用する側で生成して解放処理を行う
// TList<TGPPointF> の使用には uses に Generics.Collections が必要
//-----------------------------------------------------------------------------
procedure TplGPResizeEllipseEx.ToRelativePosList(APosList: TList<TGPPointF>);
var
LRatioX : Single;
LRatioY : Single;
LPointF : TGPPointF;
LIndex : Integer;
begin
if not Assigned(APosList) then Exit;
LRatioX := Width / FOriginWidth;
LRatioY := Height / FOriginHeight;
for LIndex := 0 to APosList.Count - 1 do begin
LPointF.X := DrawLeft + AddExtend + SignX * LRatioX * APosList[LIndex].X;
LPointF.Y := DrawTop + AddExtend + SignY * LRatioY * APosList[LIndex].Y;
APosList[LIndex] := LPointF;
end;
end;
DrawRoutine メソッド内の,直線を描画するコードを以下のようにします. |
FPosList.Clear;
LPosF.X := 25.0;
LposF.Y := 50.0;
FPosList.Add(LPosF);
LPosF.X := 25.0;
LposF.Y := FOriginHeight;
FPosList.Add(LPosF);
// 座標値を変換して描画
ToRelativePosList(FPosList);
LGPGraphic.DrawLine(LGPPen, FPosList[0].X, FPosList[0].Y, FPosList[1].X, FPosList[1].Y);
実行結果です.ZIP 書庫に添付のサンプルで確認できます. |
 |
図14
生成してリサイズ
- 縦の直線の描画始点 Y 座標は,描画用コトンロールの高さの半分
|
 |
図15
ラバーバンドの高さを 50 以下にする
- 縦の直線の描画始点 Y 座標は,描画用コトンロールの高さの半分
これは上の図と同じ
|
楕円の描画にも座標値のリストを使用する場合は,例えば以下のようになります.MakeRect 関数の第 3 引数と第 4 引数の値は座標値ではなく,幅と高さです. |
FPosList.Clear;
// 楕円の端点
LPosF.X := 0.0;
LPosF.Y := 0.0;
FPosList.Add(LPosF);
LPosF.X := FOriginWidth;
LPosF.Y := FOriginHeight;
FPosList.Add(LPosF);
// 縦の直線の短点
LPosF.X := 25.0;
LposF.Y := 50.0;
FPosList.Add(LPosF);
LPosF.X := 25.0;
LposF.Y := FOriginHeight;
FPosList.Add(LPosF);
// 座標値を変換
ToRelativePosList(FPosList);
// 楕円の描画用の TRectF を設定
LGPRect := MakeRect(FPosList[0].X, FPosList[0].Y,
FPosList[1].X - FPosList[0].X, FPosList[1].Y - FPosList[0].Y);
// 楕円を描画
LGPGraphic.DrawEllipse(LGPPen, LGPRect);
// 縦の直線の描画
LGPGraphic.DrawLine(LGPPen, FPosList[2].X, FPosList[2].Y, FPosList[3].X, FPosList[3].Y);
|