|
画像・図形の移動リサイズ関係サンプル
R010_描画用移動リサイズコントロールの基本クラス |
動作確認等 |
Windows 10 ビルド 19045 Pro 64 bit + Delphi XE5(UP2) Pro VCL-32
掲載画像は Windows 7 U64(SP1) + Delphi XE(UP1) Pro |
 |
R010_BaseDragResize.zip [2.9 MB] 2025年03月16日版
描画用クラスのソースコードは [概要] ページからダウンロードする |
基本クラスは TGraphicControl の継承クラス |
描画用の基本クラスのテストです.基本クラスのクラス名は plBaseDragResize です.
このクラスには,移動とリサイズのためのラバーバンドとグラブハンドルの表示と,移動リサイズの制御のためのコードを実装しています.実際の描画コードはありません.描画コードは,このクラスを継承したクラスで実装します.
画像や図形の描画でよく利用するのは TGraphicControl の派生クラスである
TImage コントロールですが,この基本クラスも TGraphicControl の派生クラスです.ただし,TImage のような機能は実装していません.また,コンポーネントとして登録するためのコードはありません.実行時に生成して利用する方式となっています.
DispOnly プロパティの値を True にすると,ラバーバンドもグラブハンドルも表示しなくなり,マウスでの移動リサイズができなくなります.単なる描画用のコントロールとなります. |
下図は,基本クラスのコントロールだけを TPanel に配置して表示した結果です.画像の一部を選択するラバーバンドとしても使用可能です.
|

|
図1
描画コードなしで基本クラスのコントロールだけを表示 |
 |
図2
GrabCornerOnly プロパティの値を True にすると 4 隅のグラブハンドルだけを表示 |
 |
図3
GrabHorzOnly プロパティが True の場合 |
 |
図4
GrabVertOnly プロパティが True の場合 |
リスト1
描画コードがない基本クラスのコントロールを TPanel に表示 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, plBaseDragResize;
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
FDragResize : TplBaseDragResize;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
// TPanelのDoubleBufferedの値をTrueにしている
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
Panel1.Caption := '';
Panel2.Caption := '';
FDragResize := TplBaseDragResize.Create(Self);
FDragResize.Parent := Panel2;
FDragResize.Selected := True;
FDragResize.Width := 130;
FDragResize.Height := 130;
FDragResize.InverseDisp := True;
end;
//=============================================================================
// フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
if Assigned(FDragResize) then FreeAndNil(FDragResize);
end;
//=============================================================================
// GrabCornerOnlyプロパティの動作確認
// グラブハンドルを4隅だけにする
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
if Assigned(FDragResize) then begin
FDragResize.GrabCornerOnly := not FDragResize.GrabCornerOnly;
end;
end;
//=============================================================================
// GrabHorzOnlyプロパティの動作確認
// グラブハンドルを水平方向のリサイズ用だけにする
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
if Assigned(FDragResize) then begin
FDragResize.GrabHorzOnly := not FDragResize.GrabHorzOnly;
end;
end;
//=============================================================================
// GrabVertOnlyプロパティの動作確認
// グラブハンドルを垂直方向のリサイズ用だけにする
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
if Assigned(FDragResize) then begin
FDragResize.GrabVertOnly := not FDragResize.GrabVertOnly;
end;
end;
end.
基本クラスである plBaseDragResize で実装しているプロパティです.TGraphciControl のプロパティも使用できます.
専用のコンテナ用のコントロールでは,複数の描画コントロールを選択すると,Mousethrough を自動的に Trueにするようにしています.選択する描画コントロールが 1 つになると,全ての描画コントロールの Mousethrough プロパティの値は False になります.
AntiAliasing プロパティは,継承クラスで操作を処理しないと機能しません.
|
プロパティ |
型 |
説明 |
Selected |
Boolean |
描画コントロールの選択用プロパティ
True にするとラバーバンドとグラブハンドルを表示して選択状態となりマウスでの移動リサイズが可能になる
デフォルトは False |
InverseDisp |
Boolean |
True にすると,リサイズの際,左右,上下を反転した描画を行う
デフォルトは False |
MoveOnly |
Boolean |
True にするとグラブハンドルを非表示にして移動だけを可能にする
デフォルトは False |
DispOnly |
Boolean |
True にするとラバーバンドとグラブハンドルが非表示となり,マウスによる移動リサイズは不可となる
マウスイベントは発生する.独自のマウスイベント処理の実装用
デフォルトは False |
Mousethrough |
Boolean |
True にするとマウスイベントを透過する
デフォルトは False |
AntiAliasing |
Boolean |
アンチエイリアス処理の有無
実際の処理は継承クラスで実装する
デフォルトの値は True |
下表はラバーバンドとリサイズ用のグラブハンドル関係の設定プロパティです.
グラブハンドルは矩形です.一般的にリサイズだけ機能の時はグラブハンドルの形状は矩形で,回転の機能がある時は円が使われています.
GrabCornerOnly, GrabHorz, GrabVertOnly プロパティは,後からの設定が有効となります. |
プロパティ |
型 |
説明 |
RubberBand |
TShape |
ラバーバンドオブジェクト
デフォルトの設定はソースコード参照 |
GrabBrushColor |
TColor |
リサイズ用グラブハンドルの矩形の塗り潰しの色
デフォルトは clWhite |
GrabCornerOnly |
Boolean |
True にすると 4 隅のグラブハンドルだけを表示する
デフォルトは False |
GrabHorzOnly |
Boolean |
True に横方向のリサイズ用グラブハンドルだけを表示する
デフォルトは False |
GrabVertOnly |
Boolean |
True にすると縦方向のリサイズ用グラブハンドルだけを表示する
デフォルトは False |
2025年3月以降の更新で以下のプロパティを追加しました.拡張描画領域の意味と使用例については,次ページ以降のサンプルプログラムの記事で確認してください. |
プロパティ |
型 |
説明 |
UseExtend |
Boolean |
拡張描画領域の使用有無
拡張描画領域を使用する時は True にする.デフォルトは False
描画用クラスのコンストラクタ (Create メソッド) で設定する |
AddExtend |
Integer |
拡張描画領域のサイズ
描画領域が,縦,横共に AddExtend x 2 だ大きくなる.デフォルトは 0
描画用クラスのコンストラクタまたは DrawRoutine で設定する |
SignX |
Integer |
X 軸方向の反転状態.1 は反転なし.-1 は反転状態 |
SignY |
Integer |
Y 軸方向の反転状態.1 は反転なし.-1 は反転状態 |
基本クラスである plBaseDragResize で実装しているメソッド類です.TGraphciControl のメソッド類も使用できます. |
メソッド名 |
戻り値 |
説明 |
DrawRoutine |
- |
描画用のメソッド
引数付きの Paint と機能は同じ |
BringToFron |
- |
描画コントロールを Z オーダの最前面に移動する
ラバーバンドとグラブハンドルも操作する必要があるので実装
複数の描画コントロールを使用する際に必要 |
SendToBack |
- |
描画コントロールを Z オーダの最背面に移動する
実装の意味は上記と同じ |
SetBounds |
- |
描画コントロールの SetBountds メソッド
実装の意味は上記と同じ |
ResetInverseDisp |
- |
このメソッドを実行すると反転描画の状態を解除する |
2025年3月の更新で以下のメソッド類を追加しました.使用例については,次ページ以降のサンプルプログラムの記事を参考にしてください. |
メソッド名 |
戻り値 |
説明 |
GetGPBitma |
TGPBitmap |
s 現在表示中の画像または図形を TGPBitmap として取得
DrawRoutine 内では使用できない |
SetColorToAlphaZero |
- |
TBitmap の指定職を透明色 ( アルファ値を 0 ) にする |
BitmapToGPBitmap |
常に True |
TBitmap を TGPBitmap に変換
TBitmap のアルファ値も処理 |
GPBitmapToBitmap |
常に True |
TGPBitmap を TBitmap に変換 |
GetExtendGPbitmap |
TGPBitmap |
実際に描画データがある部分だけの TGPBitmap を取得
拡張描画領域を使用する場合に使用
拡張描画利用域を使用しない時は元の TGPBitmap のまま |
SetBitmapCanvas_forDraw |
- |
DrawRoutine の描画コードが VCL の時の専用メソッド
描画を TGPBitmap に対して実行する際の前処理用 |
BitmapToGPBitmap_forDra |
- |
DrawRoutine の描画コードが VCL の時の専用メソッド
描画を TGPBitmap に対して実行する際の後処理用 |
03_継承クラスに実装する描画用メソッド DrawRoutine |
描画用の基本クラスには DrawRoutine という描画メソッドを実装しています.このメソッドの機能は引数付きの Paint メソッドと同じです.この継承メソッドを実装した場合,最初の引数 TCanvas は描画コントロール自身の TCanvas です.
TImage では再描画が必要になると自動的に再描画が行われますが,本描画コントロールは TPaintBox と同じ様に,Invalidate メソッドを実行して再描画する必要があります.Invalidate を実行することにより,Paint メソッドが呼び出されて,この DrawRoutine も実行されます.
|
DrawRoutine(ACanvas, AGPBitmap, AOffsetX, AOffsetY)
引数 |
型 |
説明 |
ACanvas |
TCanvas |
描画先の TCanas.通常は自分自身の TCanvas
TGPBitmap に描画する時はこの引数の値を nil にする |
AGPBitmap |
TGPBitmap |
TGPBitmap に描画する時に指定する
TGPBitmap に描画する時は ACanvas の値を nil にする |
AOffsetX |
Integer |
描画開始位置の横方向のオフセット
通常は 0 |
AOffsetY |
Integer |
描画開始位置の盾方向のオフセット
通常は 0 |
描画用の座標値関係として以下のプロパティを用意しています.これらは描画の上下左右が反転する場合に利用できるようにするために実装しています.必要がなければ使用する必要はありません.
全て読み出し専用のプロパティです.
描画コントロール (ラバーバンド) の親コントロールにおける位置は,Left, Tpp プロパティで取得できます. |
プロパティ |
型 |
説明 |
DrawRect |
TRect |
描画領域の TRect レコード型 |
DrawLeft |
Integer |
描画領域の左端 X 座標値.通常は 0
リサイズで左右が反転すると DrawRight の値と入れ替わる |
DrawTop |
Integer |
描画領域の上端 Y 座標値.通常は 0
リサイズで上下が反転すると DrawBottom の値と入れ替わる |
DrawRight |
Integer |
描画領域の右端 X 座標値.通常は Width と同じ値
リサイズで左右が反転すると DrawLeft の値と入れ替わる |
DrawBottom |
Integer |
描画領域の下端 Y 座標値.通常は Height と同じ値
リサイズで上下が反転すると DrawTop の値と入れ替わる |
DrawWidth |
Integer |
描画領域の幅で,絶対値は描画コントロールの幅
左右が反転している場合は負の値 |
DrawHeight |
Integer |
描画領域の高さで,絶対値は描画コントロールの高さ
上下が反転している場合は負の値 |
図形描画用の継承クラスの作成例です.単に左上から右下に直線を描画するだけです.
TMyDragResize というクラスを作成して,実行時にそのクラスのインスタンスを生成します.描画用の DrawRoutine メソッドを継承したメソッドだけを実装しています.必要であれば色や線の太さの設定用のプロパティなりメソッドを実装することになります.
|
 |
図5
設計時の画面 |
 |
図6
EXE 実行開始時
|
 |
図7
右方向にリサイズして左右反転 |
 |
図8
描画コントロールは透明
|
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, GDIPOBJ, GDIPAPI, plBaseDragResize;
type
TMyDragResize = class(TplBaseDragResize)
public
procedure DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap = nil;
AOffsetX: Integer = 0; AOffsetY: Integer = 0); override;
end;
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
ColorDialog1: TColorDialog;
Panel2: TPanel;
CheckBox1: TCheckBox;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
private
{ Private 宣言 }
FDragResize1 : TMyDragResize;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
// TPanelのDoubleBufferedの値をTrueにしている
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
Panel1.Caption := '';
Panel2.Caption := '';
end;
//=============================================================================
// フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
if Assigned(FDragResize1) then FreeAndNil(FDragResize1);
end;
//=============================================================================
// 移動リサイズの基本クラスのコントロールを生成
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
FormDestroy(nil);
CheckBox1.Checked := True;
FDragResize1 := TMyDragResize.Create(Self);
FDragResize1.Parent := Panel2;
FDragResize1.Selected := True;
FDragResize1.Width := 130;
FDragResize1.Height := 130;
FDragResize1.InverseDisp := True;
end;
//=============================================================================
// 背景色変更
// コントロールの矩形領域内が透明であることの確認
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
if ColorDialog1.Execute then begin
Panel2.Color := ColorDialog1.Color;
end;
end;
//=============================================================================
// マウスではなくコードでコントロールの選択,選択解除を操作
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if Assigned(FDragResize1) then begin
FDragResize1.Selected := CheckBox1.Checked;
end;
end;
{ TMyDragResize }
//=============================================================================
// 描画メソッド
//=============================================================================
procedure TMyDragResize.DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap;
AOffsetX: Integer; AOffsetY: Integer);
begin
inherited;
with ACanvas do begin
Pen.Width := 5;
Pen.Color := clBlue;
MoveTo(DrawLeft, DrawTop);
LineTo(DrawRight, DrawBottom);
end;
end;
end.
上のサンプルプログラムを実行して描画コントロールをリサイズすると,上下左右の反転描画ができることが確認できます.
次のように描画関係の座標値を具体的な数値で指定した場合は,正常に反転描画できません. |
 |
図9
反転描画が正常にできない例 |
 |
図10
元の図形とは違う図形となってしまう |
リスト3
反転描画が正常にできないコーディングの例 |
//=============================================================================
// 描画メソッド
// 反転描画が正常にできないコード
//=============================================================================
procedure TMyDragResize.DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap;
AOffsetX: Integer; AOffsetY: Integer);
begin
inherited;
with ACanvas do begin
Pen.Width := 5;
Pen.Color := clBlue;
MoveTo(DrawLeft + 20, DrawTop + 20);
LineTo(DrawRight - 20, DrawBottom - 20);
end;
end;
移動リサイズ用の描画コントロールですから,具体的な数値で描画の座標を指定することはあまり考えられません.次のように,この基本クラスで用意した Draw... というプロパティを使用してコーディングすれば,リサイズの時の反転描画を正常に実行することが可能になります. |
 |
図11
反転描画が正常に実行できる例 |
 |
図12
リサイズして上下を反転した状態 |
リスト4
図形描画で反転描画を正常に実行するためのコーディングの例 |
//=============================================================================
// 描画メソッド
// 正常に反転表示が可能なコード
//=============================================================================
procedure TMyDragResize.DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap;
AOffsetX: Integer; AOffsetY: Integer);
var
LDx1 : Integer;
LDy1 : Integer;
LDx2 : Integer;
LDy2 : Integer;
begin
inherited;
LDx1 := DrawWidth div 5;
LDy1 := DrawHeight div 5;
LDx2 := DrawWidth div 9;
LDy2 := DrawHeight div 9;
with ACanvas do begin
Pen.Width := 5;
Pen.Color := clBlue;
MoveTo(DrawLeft + LDx1, DrawTop + LDy1);
LineTo(DrawRight - LDx2, DrawBottom - LDy2);
end;
end;
画像描画用の継承クラスの作成例です.TMyDragResize というクラスを作成して,実行時にそのクラスのインスタンスを生成します.GDI+ の TGPBitmap の画像描画用のクラスです.
描画用の DrawRoutine メソッドを継承したメソッドだけを実装しています.必要であれば TImage のように,画像ファイルの読み込み用のメソッド等を追加することになります.
|
 |
図13
TGPBitmap の描画例
|
リスト5
画像を描画するための継承クラスの簡単な例 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, GDIPOBJ, GDIPAPI, plBaseDragResize;
type
//画像を描画するクラスの例
TMyDragResize = class(TplBaseDragResize)
public
procedure DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap = nil;
AOffsetX: Integer = 0; AOffsetY: Integer = 0); override;
end;
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
ColorDialog1: TColorDialog;
Panel2: TPanel;
CheckBox1: TCheckBox;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
private
{ Private 宣言 }
FDragResize1 : TMyDragResize;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
// TPanelのDoubleBufferedの値をTrueにしている
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
Panel1.Caption := '';
Panel2.Caption := '';
end;
//=============================================================================
// フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
if Assigned(FDragResize1) then FreeAndNil(FDragResize1);
end;
//=============================================================================
// 移動リサイズの基本クラスのコントロールを生成
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
FormDestroy(nil);
CheckBox1.Checked := True;
FDragResize1 := TMyDragResize.Create(Self);
FDragResize1.Parent := Panel2;
FDragResize1.Selected := True;
FDragResize1.InverseDisp := True;
end;
//=============================================================================
// 背景色変更
// コントロールの矩形領域内が透明であることの確認
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
if ColorDialog1.Execute then begin
Panel2.Color := ColorDialog1.Color;
end;
end;
//=============================================================================
// マウスではなくコードでコントロールの選択,選択解除を操作
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if Assigned(FDragResize1) then begin
FDragResize1.Selected := CheckBox1.Checked;
end;
end;
{ TMyDragResize }
//=============================================================================
// 描画メソッド
//=============================================================================
procedure TMyDragResize.DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap;
AOffsetX: Integer; AOffsetY: Integer);
var
LGPGraphic : TGPGraphics;
LGPBitmap : TGPBitmap;
begin
inherited;
LGPGraphic := TGPGraphics.Create(ACanvas.Handle);
LGPBitmap := TGPBitmap.Create(ExpandFileName('..\..\Images\502.gif'));
LGPGraphic.DrawImage(LGPBitmap,
MakeRect(DrawLeft, DrawTop, DrawWidth, DrawHeight),
0,
0,
LGPBitmap.GetWidth,
LGPBitmap.GetHeight,
UnitPixel);
FreeAndNil(LGPGraphic);
FreeAndNil(LGPBitmap);
end;
end.
ビットマップ画像を描画する場合は,例えば以下のようなコーディングとなります.
TBitmap の場合,アルファチャンネルの処理を有効にすると反転描画はできません. |
リスト6
画像を描画するための継承クラスの実装例.ビットマップ画像を描画 |
//=============================================================================
// 描画メソッド
//=============================================================================
procedure TMyDragResize.DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap;
AOffsetX: Integer; AOffsetY: Integer);
var
LBimap : TBitmap;
begin
inherited;
LBimap := TBitmap.Create;
try
//アルファチャンネルの処理を有効にすると反転描画は正常に実行されない
//LBimap.AlphaFormat := afDefined;
LBimap.LoadFromFile(ExpandFileName('..\..\Images\503.bmp'));
ACanvas.StretchDraw(DrawRect, LBimap);
finally
FreeAndNil(LBimap);
end;
end;
06_GDI+ の機能を使用した画像の左右または上下反転 |
この描画コントロールの基本クラスでは,反転描画を座標値の反転という状態で実現しています.ある意味では反則技ともい言えます.画像処理本来の動作仕様しては,以下のようにするのが本来の方法であると思われます.
この方法で画像の反転処理をした場合は Draw... 等のプロパティは使用しないようにします.
|
リスト7
GDI+ の RotateFlip メソッドを使用した画像の反転表示の例 |
//=============================================================================
// 描画メソッド
// GDI+のRotateFlipメソッドによる画像の回転
//=============================================================================
procedure TMyDragResize.DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap;
AOffsetX: Integer; AOffsetY: Integer);
var
LGPGraphic : TGPGraphics;
LGPBitmap : TGPBitmap;
begin
inherited;
LGPGraphic := TGPGraphics.Create(ACanvas.Handle);
LGPBitmap := TGPBitmap.Create(ExpandFileName('..\..\Images\502.gif'));
//左右反転
if DrawWidth < 0 then begin
LGPBitmap.RotateFlip(Rotate180FlipY);
end;
//上下反転
if DrawHeight < 0 then begin
LGPBitmap.RotateFlip(Rotate180FlipX);
end;
LGPGraphic.DrawImage(LGPBitmap,
MakeRect(0, 0, Width, Height),
0,
0,
LGPBitmap.GetWidth,
LGPBitmap.GetHeight,
UnitPixel);
FreeAndNil(LGPGraphic);
FreeAndNil(LGPBitmap);
end;
描画用に DrawRoutine というメソッドを実装していますが,TPaintBox のように,Paint メソッドで描画することもできます.DrawRoutine メソッドの引数を利用する必要がなければどちらのメソッドを使用しても結果は同じです. |
//=============================================================================
// Paintメソッド
//=============================================================================
procedure TMyDragResize.Paint;
var
LGPGraphic : TGPGraphics;
LGPBitmap : TGPBitmap;
begin
inherited;
LGPGraphic := TGPGraphics.Create(Canvas.Handle);
LGPBitmap := TGPBitmap.Create(ExpandFileName('..\..\Images\502.gif'));
LGPGraphic.DrawImage(LGPBitmap,
MakeRect(DrawLeft, DrawTop, DrawWidth, DrawHeight),
0,
0,
LGPBitmap.GetWidth,
LGPBitmap.GetHeight,
UnitPixel);
FreeAndNil(LGPGraphic);
FreeAndNil(LGPBitmap);
end;
この描画用コントロールは,幅または高さを 0 または負の値にできないようにしています.0 または負の値に設定しようとする強制的に 1 にします.幅または高さを 0 にすると描画コントロールが選択できなくなります.その対策処理です.
また,添付のコンテナ用コントロールではキー操作でリサイズできるようになっていますが,同じ理由で,1 未満の幅,高さには設定できないようにしています.
|
09_描画用コントロールの基本クラスのソースコード |
描画用コントロールの基本クラスのソースコードです. |
リスト9
移動リサイズ可能な描画コントロールの基本クラスのソースコード
このユニットファイルをライブラリのパスが通った場所に置いて使用する |
unit plBaseDragResize;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Menus, ExtCtrls,
Forms, GDIPOBJ, GDIPAPI;
const
WM_CREATED_RESIZECONTROL = WM_APP+10;
type
TplBaseDragResize = class;
TplBaseExtendCtrl = class(TGraphicControl)
private
FBaseCtrl : TplBaseDragResize;
public
constructor Create(AOwner: TComponent); override;
protected
procedure CMHitTest(var Message: TCMHitTest); message CM_HITTEST;
procedure Paint; override;
end;
TplBaseRubberBand = class(TShape)
protected
procedure CMHitTest(var Message: TCMHitTest); message CM_HITTEST;
public
constructor Create(AOwner: TComponent); override;
end;
TplBaseGrabHandle = class(TShape) private
protected
FResizeBase : TplBaseDragResize;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X: Integer;
Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X: Integer; Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X: Integer;
Y: Integer); override;
procedure CMHitTest(var Message: TCMHitTest); message CM_HITTEST;
public
constructor Create(AOwner: TComponent); override;
end;
TplBaseDragResize = class(TGraphicControl)
private
FGrabPoints : array [0..7] of TPoint;
FGrabHandle : array [0..7] of TShape;
FRubberBand : TplBaseRubberBand;
FGrabHandleSize : Integer;
FGrabBrushColor : TColor;
FGrabCornerOnly : Boolean;
FGrabHorzOnly : Boolean;
FGrabVertOnly : Boolean;
FGrabIndex : Integer;
FOperateMouse : Boolean;
FIsInSelfRect : Boolean;
FCurrentX : Integer;
FCurrentY : Integer;
FDrawRect : TRect;
FDrawLeft : Integer;
FDrawTop : Integer;
FDrawRight : Integer;
FDrawBottom : Integer;
FDrawWidth : Integer;
FDrawHeight : Integer;
FInverseHolz : Boolean;
FInverseVert : Boolean;
FSelected : Boolean;
FMousethrough : Boolean;
FMoveOnly : Boolean;
FDispOnly : Boolean;
FInverseDisp : Boolean;
FZoomValue : Integer;
FExitMouseEvent : Boolean;
FAntiAliasing : Boolean;
FEnabledUseMsg : Boolean;
FParentIsScrBox : Boolean;
FParentAutoScr : Boolean;
FSignX : Integer;
FSignY : Integer;
FExtendCtrl : TplBaseExtendCtrl;
FUseExtend : Boolean;
FAddExtend : Integer;
procedure SetFSelected(const Value: Boolean);
procedure SetFMoveOnly(const Value: Boolean);
procedure SetFAntiAliasing(const Value: Boolean);
procedure SetFZoomValue(const Value: Integer);
procedure SetFDispOnly(const Value: Boolean);
procedure SetFGrabBrushColor(const Value: TColor);
procedure SetFGrabHandleSize(const Value: Integer);
procedure SetFGrabCornerOnly(const Value: Boolean);
procedure SetFGrabHorzOnly(const Value: Boolean);
procedure SetFGrabVertOnly(const Value: Boolean);
procedure SetFExitMouseEvent(const Value: Boolean);
function GetFExtendRect: TRect;
function GetExtendPosSize(const Value: Integer): Integer;
function GetRealRect(AGPBitmap: TGPBitmap): TRect;
procedure SetFInverseHolz(const Value: Boolean);
procedure SetFInverseVert(const Value: Boolean);
protected
procedure Paint; override;
procedure RectToGrabHandlePoints(ARect: TRect);
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X: Integer;
Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X: Integer; Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X: Integer;
Y: Integer); override;
procedure SetParent(AParent: TWinControl); override;
procedure DoContextPopup(MousePos: TPoint; var Handled: Boolean); override;
procedure CMHitTest(var Message: TCMHitTest); message CM_HITTEST;
procedure SetRubberBandGrabHandlePosSize;
procedure CMVisiblechanged(var Message: TMessage);
message CM_VISIBLECHANGED;
procedure WMLButtonDblClk(var Message: TWMLButtonDblClk);
message WM_LBUTTONDBLCLK;
procedure Loaded; override;
property ZoomValue : Integer read FZoomValue write SetFZoomValue;
property ExitMouseEvent : Boolean read FExitMouseEvent write SetFExitMouseEvent;
public
constructor Create(AOwner: TComponent); override;
procedure BringToFront(AControl: TGraphicControl = nil);
procedure SendToBack(AControl: TGraphicControl = nil);
procedure SetBounds(ALeft: Integer; ATop: Integer; AWidth: Integer;
AHeight: Integer); override;
procedure ResetInverseDisp;
procedure DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap = nil;
AOffsetX: Integer = 0; AOffsetY: Integer = 0); virtual;
procedure Invalidate; override;
property OnDblClick;
property PopupMenu;
property RubberBand : TplBaseRubberBand read FRubberBand;
property Selected : Boolean read FSelected write SetFSelected;
property DrawRect : TRect read FDrawRect;
property DrawLeft : Integer read FDrawLeft;
property DrawTop : Integer read FDrawTop;
property DrawRight : Integer read FDrawRight;
property DrawBottom : Integer read FDrawBottom;
property DrawWidth : Integer read FDrawWidth;
property DrawHeight : Integer read FDrawHeight;
function GetGPBitmap: TGPBitmap;
procedure SetColorToAlphaZero(ABitmap: TBitmap; TransColor: Cardinal);
function BitmapToGPBitmap(SrcBitmap: TBitmap; var DstGPBitmap: TGPBitmap;
PixelFmt: TPixelFormat = PixelFormat32bppARGB): Boolean;
function GPBitmapToBitmap(SrcGPBitmap: TGPBitmap; DstBitmap: TBitmap): Boolean;
function GetRegionFromGPBitmap(GDPBitmap: TGPBitmap; GDPRegion: TGPRegion): Boolean;
function GetExtendGPbitmap(AGPBitmap: TGPBitmap): TGPBitmap;
procedure SetBitmapCanvas_forDraw(ABitmap: TBitmap; var ACanvas: TCanvas;
ATransColor: TColor);
procedure BitmapToGPBitmap_forDraw(ABitmap: TBitmap; AGPBitmap: TGPBitmap;
ATransColor: TColor; AOffsetX: Integer; AOffsetY: Integer);
property UseExtend : Boolean read FUseExtend write FUseExtend;
property AddExtend : Integer read FAddExtend write FAddExtend;
property ExtendCtrl : TplBaseExtendCtrl read FExtendCtrl write FExtendCtrl;
property ExtendRect : TRect read GetFExtendRect;
property ExtendLeft : Integer index 0 read GetExtendPosSize;
property ExtendTop : Integer index 1 read GetExtendPosSize;
property ExtendRight : Integer index 2 read GetExtendPosSize;
property ExtendBottom : Integer index 3 read GetExtendPosSize;
property ExtendWidth : Integer index 4 read GetExtendPosSize;
property ExtendHeight : Integer index 5 read GetExtendPosSize;
property SignX : Integer read FSignX;
property SignY : Integer read FSignY;
published
property GrabBrushColor : TColor read FGrabBrushColor write SetFGrabBrushColor;
property GrabHandleSize : Integer read FGrabHandleSize write SetFGrabHandleSize;
property GrabCornerOnly : Boolean read FGrabCornerOnly write SetFGrabCornerOnly;
property GrabHorzOnly : Boolean read FGrabHorzOnly write SetFGrabHorzOnly;
property GrabVertOnly : Boolean read FGrabVertOnly write SetFGrabVertOnly;
property MoveOnly : Boolean read FMoveOnly write SetFMoveOnly;
property DispOnly : Boolean read FDispOnly write SetFDispOnly;
property Mousethrough : Boolean read FMousethrough write FMousethrough;
property AntiAliasing : Boolean read FAntiAliasing write SetFAntiAliasing;
property InverseDisp : Boolean read FInverseDisp write FInverseDisp;
property InverseHolz : Boolean read FInverseHolz write SetFInverseHolz;
property InverseVert : Boolean read FInverseVert write SetFInverseVert;
end;
implementation
uses
Types,
Math;
{ TplBaseDragResize }
type
TScrollCtrl = class(TScrollBox);
//=============================================================================
// コンストラクタ
//=============================================================================
constructor TplBaseDragResize.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ParentColor := False;
Canvas.Brush.Style := bsClear;
Width := 100;
Height := 100;
FOperateMouse := False;
FEnabledUseMsg := False;
FSelected := False;
FMousethrough := False;
FMoveOnly := False;
FDispOnly := False;
FInverseDisp := False;
FAntiAliasing := True;
FParentIsScrBox := False;
FExitMouseEvent := False;
FZoomValue := 100;
FGrabHandleSize := 8;
FGrabBrushColor := clWhite;
FGrabCornerOnly := False;
FGrabHorzOnly := False;
FGrabVertOnly := False;
FGrabIndex := -1;
FIsInSelfRect := False;
FInverseHolz := False;
FInverseVert := False;
FUseExtend := False;
FAddExtend := 0;
Self.Cursor := crSizeAll;
end;
//=============================================================================
// SetParentメソッド処理
//=============================================================================
procedure TplBaseDragResize.SetParent(AParent: TWinControl);
var
LGrabNo : Integer;
LShape : TplBaseGrabHandle;
begin
inherited;
if not Assigned(AParent) then exit;
if Assigned(FRubberBand) then Exit;
//ラバーバンド生成
FRubberBand := TplBaseRubberBand.Create(Self);
FRubberBand.Parent := Parent;
FRubberBand.Brush.Style := bsClear;
FRubberBand.Pen.Width := 1;
FRubberBand.Pen.Style := psDot;
FRubberBand.Visible := False;
//グラブハンドルの生成
//各グラブハンドルのマウスカーソルの設定
for LGrabNo := 0 to Length(FGrabHandle) - 1 do begin
LShape := TplBaseGrabHandle.Create(Self);
LShape.Parent := Parent;
LShape.Width := FGrabHandleSize;
LShape.Height := FGrabHandleSize;
LShape.Visible := False;
if (LGrabNo = 0) or (LGrabNo = 4) then begin
LShape.Cursor := crSizeNWSE;
end else
if (LGrabNo = 2) or (LGrabNo = 6) then begin
LShape.Cursor := crSizeNESW;
end else
if (LGrabNo = 1) or (LGrabNo = 5) then begin
LShape.Cursor := crSizeNS;
end else
if (LGrabNo = 3)or (LGrabNo = 7)then begin
LShape.Cursor := crSizeWE;
end;
FGrabHandle[LGrabNo] := LShape;
end;
FParentIsScrBox := Parent is TScrollBox;
if FParentIsScrBox then begin
FParentAutoScr := TScrollCtrl(Parent).AutoScroll;
end;
//描画コントロールが使用可能になったことを親に通知する
if not FEnabledUseMsg then begin
FEnabledUseMsg := True;
PostMessage(Parent.Handle, WM_CREATED_RESIZECONTROL, WPARAM(Self), 0);
end;
if UseExtend and not Assigned(FExtendCtrl) then begin
FExtendCtrl := TplBaseExtendCtrl.Create(Self);
FExtendCtrl.Parent := AParent;
BringToFront(FExtendCtrl);
end;
end;
//=============================================================================
// Loaded メソッド処理
// Selected プロパティを False にする
//=============================================================================
procedure TplBaseDragResize.Loaded;
begin
inherited;
FSelected := False;
end;
//-----------------------------------------------------------------------------
// CMVisiblechangedメッセージ処理
// Vislbleプロパティが変更された時に発生するメソッドー
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.CMVisiblechanged(var Message: TMessage);
begin
inherited;
if Assigned(FExtendCtrl) then begin
FExtendCtrl.Visible := Visible;
end;
//ラバーバンドとグラブハンドルの表示・非表示も変更する
SetRubberBandGrabHandlePosSize;
end;
//-----------------------------------------------------------------------------
// GrabBrushColorプロパティの設定用メソッド
// グラブハンドルの矩形の塗り潰しの色変更
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetFGrabBrushColor(const Value: TColor);
var
LGrabNo : Integer;
begin
if not Assigned(FRubberBand) then exit;
if FGrabBrushColor <> Value then begin
FGrabBrushColor := Value;
for LGrabNo := 0 to Length(FGrabHandle) - 1 do begin
FGrabHandle[LGrabNo].Brush.Color := FGrabBrushColor;
end;
end;
end;
//-----------------------------------------------------------------------------
// GrabHandleSizeプロパティの設定用メソッド
// グラブハンドルのサイズの設定
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetFGrabHandleSize(const Value: Integer);
var
LGrabNo : Integer;
begin
if not Assigned(FRubberBand) then exit;
if FGrabHandleSize <> Value then begin
FGrabHandleSize := Value;
for LGrabNo := 0 to Length(FGrabHandle) - 1 do begin
FGrabHandle[LGrabNo].Width := FGrabHandleSize;
FGrabHandle[LGrabNo].Height := FGrabHandleSize;
end;
end;
end;
//=============================================================================
// DoContextPopupメソッド
// 選択状態でない時はポップアップメニューを表示しない
//=============================================================================
procedure TplBaseDragResize.DoContextPopup(MousePos: TPoint;
var Handled: Boolean);
var
LPos : TPoint;
begin
inherited;
if not Assigned(PopupMenu) then exit;
PopupMenu.AutoPopup := False;
if FSelected then begin
LPos := ClientToScreen(MousePos);
PopupMenu.Popup(LPos.X, LPos.Y);
end else begin
Handled := True;
end;
end;
//-----------------------------------------------------------------------------
// Selectedプロパティの設定メソッド
// 選択・選択解除のプロパティ
// Trueにするとラバーバンドとグラブハンドルを表示する
// Falseにするとそれらを非表示にする
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetFSelected(const Value: Boolean);
begin
if FSelected <> Value then begin
FSelected := Value;
if (FSelected = False) or FDispOnly then begin
FGrabIndex := -1;
FIsInSelfRect := False;
SetRubberBandGrabHandlePosSize;
Invalidate;
end;
end;
end;
//-----------------------------------------------------------------------------
// MoveOnlyプロパティの設定用メソッド
// Trueにするとグラブハンドルを表示しない
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetFMoveOnly(const Value: Boolean);
var
LGrabNo : Integer;
begin
if FMoveOnly <> Value then begin
FMoveOnly := Value;
for LGrabNo := 0 to Length(FGrabHandle) - 1 do begin
FGrabHandle[LGrabNo].Visible := not FMoveOnly;
end;
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// DispOnlyプロパティの設定用メソッド
// Trueにすると選択できなくなり表示専用となる
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetFDispOnly(const Value: Boolean);
begin
if FDispOnly <> Value then begin
FDispOnly := Value;
if FDispOnly then begin
SetFSelected(False);
Self.Cursor := crDefault;
end else begin
Self.Cursor := crSizeAll;
end;
end;
end;
//-----------------------------------------------------------------------------
// GrabCornerOnlyプロパティの設定用メソッド
// 4隅のグラブハンドルだけを表示する
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetFGrabCornerOnly(const Value: Boolean);
begin
if FGrabCornerOnly <> Value then begin
FGrabCornerOnly := Value;
FGrabHorzOnly := False;
FGrabVertOnly := False;
SetRubberBandGrabHandlePosSize;
end;
end;
//-----------------------------------------------------------------------------
// GrabHorzOnlyプロパティの設定用メソッド
// 水平方向リサイズ用の左右2つだけを表示
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetFGrabHorzOnly(const Value: Boolean);
begin
if FGrabHorzOnly <> Value then begin
FGrabHorzOnly := Value;
FGrabCornerOnly := False;
FGrabVertOnly := False;
SetRubberBandGrabHandlePosSize;
end;
end;
//-----------------------------------------------------------------------------
// GrabVertOnlyプロパティの設定用メソッド
// 垂直方向リサイズ用の上下2つだけを表示
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetFGrabVertOnly(const Value: Boolean);
begin
if FGrabVertOnly <> Value then begin
FGrabVertOnly := Value;
FGrabCornerOnly := False;
FGrabHorzOnly := False;
SetRubberBandGrabHandlePosSize;
end;
end;
//-----------------------------------------------------------------------------
// InverseHolzプロパティの設定用メソッド
// 左右反転状態の設定状態
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetFInverseHolz(const Value: Boolean);
begin
if FInverseHolz <> Value then begin
FInverseHolz := Value;
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// InverseVertプロパティの設定用メソッド
// 上下反転状態の設定状態
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetFInverseVert(const Value: Boolean);
begin
if FInverseVert <> Value then begin
FInverseVert := Value;
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// AntiAliasingプロパティの設定用メソッド
// アンチエイリアシング処理の設定
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetFAntiAliasing(const Value: Boolean);
begin
if FAntiAliasing <> Value then begin
FAntiAliasing := Value;
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// ExitMouseEventプロパティの設定用メソッド
// 継承クラスでのマウスイベントの実装用
// コンテナコントロールからの設定用
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetFExitMouseEvent(const Value: Boolean);
begin
if FExitMouseEvent <> Value then begin
FExitMouseEvent := Value;
end;
end;
//-----------------------------------------------------------------------------
// ZoomValueプロパティの設定用メソッド
// 拡大・縮小表示の値の設定
// 本描画コントロールには実際の拡大・縮小表示の機能はない
// コンテナ用コントロールからの操作用
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetFZoomValue(const Value: Integer);
begin
if FZoomValue <> Value then begin
FZoomValue := Value;
Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// 最前面に移動
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.BringToFront(AControl: TGraphicControl);
var
LGrabNo : Integer;
begin
TControl(Self).BringToFront;
if AControl <> nil then begin
AControl.BringToFront;
end;
FRubberBand.BringToFront;
for LGrabNo := 0 to Length(FGrabHandle) - 1 do begin
FGrabHandle[LGrabNo].BringToFront;
Application.ProcessMessages;
end;
end;
//-----------------------------------------------------------------------------
// 最背面に移動
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SendToBack(AControl: TGraphicControl = nil);
var
LGrabNo : Integer;
begin
for LGrabNo := Length(FGrabHandle) - 1 downto 0 do begin
FGrabHandle[LGrabNo].SendToBack;
Application.ProcessMessages;
end;
if AControl <> nil then begin
AControl.SendToBack;
end;
FRubberBand.SendToBack;
TControl(Self).SendToBack;
end;
//=============================================================================
// MouseDownメソッド
// 非選択状態の時は,マウス左ボタン押下で選択状態にして移動リサイズ可能にする
// 選択状態にするということは,ラバーバンドとグラブハンドルを表示すること
//
// [Ctrl]キーが押下されていなければ,既に選択中のこのクラスのコントロールを
// 非選択状態にする(ラバーバンドとグラブハンドルを非表示にする)
//=============================================================================
procedure TplBaseDragResize.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
var
LPosInParent : TPoint;
LSelfRect : TRect;
LClipRect : TRect;
LControl : TControl;
LControlNo : Integer;
LParentForm : TCustomForm;
begin
inherited;
if FDispOnly then exit;
if FExitMouseEvent then exit;
if Button <> mbLeft then exit;
FOperateMouse := True;
//キーによる操作を可能にする
//親フォーム検出のこの方法は簡便法
LParentForm := GetParentForm(Self);
if (LParentForm <> nil) and (LParentForm <> Parent) then begin
LParentForm.ActiveControl := Parent;
end;
FOperateMouse := True;
//-1の時はグラブハンドルが選択されていない状態
FGrabIndex := -1;
//マウスカーソルがグラブハンドル内にあるか調査
if not FMoveOnly then begin
for LControlNo := 0 to Length(FGrabPoints) - 1 do begin
if FGrabHandle[LControlNo].Visible then begin
if PtInRect(Bounds(FGrabPoints[LControlNo].X - 1,
FGrabPoints[LControlNo].Y - 1,
(FGrabHandleSize + 1) * 2,
(FGrabHandleSize + 1) * 2),
Point(X, Y)) then begin
FCurrentX := X;
FCurrentY := Y;
FGrabIndex := LControlNo;
Break;
end;
end;
end;
end;
//マウスカーソルがグラブハンドル内でなければ本コントロール内かを調査
LPosInParent := Self.ClientToParent(Point(X, Y));
if FGrabIndex = -1 then begin
LSelfRect := Bounds(Left, Top, Width, Height);
FIsInSelfRect := PtInRect(LSelfRect, LPosInParent);
if FIsInSelfRect then begin
FCurrentX := LPosInParent.X;
FCurrentY := LPosInParent.Y;
end;
end;
FSelected := ((FGrabIndex >= 0) or FIsInSelfRect) and not FDispOnly;
if FSelected then begin
//[Ctrl]押下時は複数選択可能とするので選択は解除しない
//複数選択時はマウス透過とし移動は親コントロールのマウスイベントで行う
if (ssCtrl in Shift) then begin
for LControlNo := 0 to Parent.ControlCount - 1 do begin
LControl := Parent.Controls[LControlNo];
if LControl Is TplBaseDragResize then begin
if (LControl <> Self) then begin
//1つでも既に選択状態のコントロールがある場合
if TplBaseDragResize(LControl).Selected then begin
TplBaseDragResize(LControl).Mousethrough := True;
FMousethrough := True;
break;
end;
end;
end;
end;
end else begin
//自分自身以外の選択を解除
//選択状態になったらマウスの移動範囲をこの親コントロール内とする
for LControlNo := 0 to Parent.ControlCount - 1 do begin
LControl := Parent.Controls[LControlNo];
if LControl Is TplBaseDragResize then begin
TplBaseDragResize(LControl).Mousethrough := False;
if (LControl <> Self) then begin
TplBaseDragResize(LControl).SetFSelected(False);
end;
end;
end;
Invalidate;
LClipRect := Parent.ClientRect;
LClipRect.TopLeft := Parent.ClientToScreen(LClipRect.TopLeft);
LClipRect.BottomRight := Parent.ClientToScreen(LClipRect.BottomRight);
ClipCursor(@LClipRect);
end;
end;
end;
//=============================================================================
// MouseMoveメソッド
//=============================================================================
procedure TplBaseDragResize.MouseMove(Shift: TShiftState; X, Y: Integer);
var
LPosInParent : TPoint;
LLeft : Integer;
LTop : Integer;
LWidth : Integer;
LHeight : Integer;
LIncX : Integer;
LIncY : Integer;
LHorzScrPos : Integer;
LVertScrPos : Integer;
LHorzScrRange : Integer;
LVertScrRange : Integer;
LPixelSize : Integer;
begin
inherited;
LPixelSize := ZoomValue div 100;
if FExitMouseEvent then exit;
if FDispOnly then exit;
//マウスカーソルはグラブハンドル内にない
if FGrabIndex = -1 then begin
//オブジェクトの矩形内にある場合
if FIsInSelfRect then begin
LPosInParent := ClientToParent(Point(X, Y));
LIncX := LPosInParent.X - FCurrentX;
LIncY := LPosInParent.Y - FCurrentY;
//親がTScrollBoxの時の処理
//最下端から上方向への移動と最右端から左方向に移動する場合
//この処理をしないと移動できない
if FParentIsScrBox and FSelected then begin
LHorzScrPos := TScrollCtrl(Parent).HorzScrollBar.Position;
LVertScrPos := TScrollCtrl(Parent).VertScrollBar.Position;
LHorzScrRange := TScrollCtrl(Parent).HorzScrollBar.Range;
LVertScrRange := TScrollCtrl(Parent).VertScrollBar.Range;
if (LIncY < 0) and (LVertScrPos > 0) then begin
if ((Top + Height+ FGrabHandleSize) >= Parent.ClientHeight) then begin
TScrollCtrl(Parent).VertScrollBar.Range := LVertScrRange - LIncY;
end;
if (Top <= 0) then begin
TScrollCtrl(Parent).VertScrollBar.Position := LVertScrPos + LIncY;
end;
end;
if (LIncX <= 0) and (LHorzScrPos > 0) then begin
if ((Left + Width + FGrabHandleSize) >= Parent.ClientWidth) then begin
TScrollCtrl(Parent).HorzScrollBar.Range := LHorzScrRange - LIncX;
end;
if (Left <= 0) then begin
TScrollCtrl(Parent).HorzScrollBar.Position := LHorzScrPos + LIncX;
end;
end;
end;
LLeft := (Left + LIncX + Width) div LPixelSize * LPixelSize;
LTop := (Top + LIncY + Height) div LPixelSize * LPixelSize;
SetBounds(LLeft - Width, LTop - Height, Width, Height);
FCurrentX := LPosInParent.X div LPixelSize * LPixelSize;
FCurrentY := LPosInParent.Y div LPixelSize * LPixelSize;
end else begin
exit;
end;
end else begin
//マウスカーソルがグラブハンドル内にある場合
LLeft := Left;
LTop := Top;
LWidth := Width;
LHeight := Height;
case FGrabIndex of
0:
begin
LLeft := Left + (X - FCurrentX);
LTop := Top + (Y - FCurrentY);
end;
1:
begin
LTop := Top + (Y - FCurrentY);
end;
2:
begin
LWidth := Width + (X - FCurrentX);
LTop := Top + (Y - FCurrentY);
end;
3:
begin
LWidth := Width + (X - FCurrentX);
end;
4:
begin
LWidth := Width + (X - FCurrentX);
LHeight := Height + (Y - FCurrentY);
end;
5:
begin
LHeight := Height + (Y - FCurrentY);
end;
6:
begin
LLeft := left + (X - FCurrentX);
LHeight := Height + (Y - FCurrentY);
end;
7:
begin
LLeft := Left + (X - FCurrentX);
end;
end;
LLeft := LLeft div LPixelSize * LPixelSize;
LTop := LTop div LPixelSize * LPixelSize;
LWidth := (LWidth + (Left - LLeft)) div LPixelSize * LPixelSize;
LHeight := (LHeight + (Top - LTop)) div LPixelSize * LPixelSize;
SetBounds(LLeft, LTop, LWidth, LHeight);
FCurrentX := X div LPixelSize * LPixelSize;
FCurrentY := Y div LPixelSize * LPixelSize;
end;
end;
//=============================================================================
// MouseUpメソッド
//
// 以下の処理
// マウスカーソル移動範囲の制限を解除
// グラブハンドルはどれも掴んでいないことにする
// オブジェクトの矩形内も掴んでいないことにする
//=============================================================================
procedure TplBaseDragResize.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
var
LLeft : Integer;
LTop : Integer;
LWidth : Integer;
LHeight : Integer;
LPixelSize : Integer;
LHorzRange : Integer;
LVertRange : Integer;
LControlNo : Integer;
LControl : TControl;
LCtrl : TplBaseDragResize;
LHorzScrPos : Integer;
LVertScrPos : Integer;
begin
inherited;
if FExitMouseEvent then exit;
if FDispOnly then exit;
LPixelSize := FZoomValue div 100;
if LPixelSize = 0 then LPixelSize := 1;
//マウスカーソルはグラブハンドル内にない
if FGrabIndex = -1 then begin
//オブジェクトの矩形内にある場合
if FIsInSelfRect then begin
LLeft := Left div LPixelSize * LPixelSize;
LTop := Top div LPixelSize * LPixelSize;
SetBounds(LLeft, LTop, Width, Height);
end;
//親のTScrollBoxのサイズを調整
//最下端から上方向への移動と最右端から左方向に移動した場合に必要
if FParentIsScrBox then begin
LHorzScrPos := TScrollCtrl(Parent).HorzScrollBar.Position;
LVertScrPos := TScrollCtrl(Parent).VertScrollBar.Position;
if (LHorzScrPos > 0) or (LVertScrPos > 0) then begin
LHorzRange := 0;
LVertRange := 0;
for LControlNo := 0 to Parent.ControlCount - 1 do begin
LControl := Parent.Controls[LControlNo];
if LControl Is TplBaseDragResize then begin
LCtrl := TplBaseDragResize(LControl);
LHorzRange := Max(LHorzRange, LHorzScrPos + LCtrl.Left + LCtrl.Width);
LVertRange := Max(LVertRange, LVertScrPos + LCtrl.Top + LCtrl.Height);
end;
end;
TScrollCtrl(Parent).HorzScrollBar.Range := LHorzRange;
TScrollCtrl(Parent).VertScrollBar.Range := LVertRange;
end;
if FParentIsScrBox then begin
TScrollCtrl(Parent).AutoScroll := FParentAutoScr;
end;
end;
end else begin
//マウスカーソルがグラブハンドル内にある場合
LWidth := Width;
LHeight := Height;
LLeft := Left div LPixelSize * LPixelSize;
LTop := Top div LPixelSize * LPixelSize;
if LLeft < Left then begin
LWidth := LWidth + (Left - LLeft);
end;
if LTop < Top then begin
LHeight := LHeight + (Top - LTop);
end;
//幅や高さを0にしてしまうとマウスで選択できなくなるのでその対策
LWidth := Max(LPixelSize, LWidth div LPixelSize * LPixelSize);
LHeight := Max(LPixelSize, LHeight div LPixelSize * LPixelSize);
SetBounds(LLeft, LTop, LWidth, LHeight);
end;
//ズームとTScrollBox関係の処理がなければ以下のコードだけでよい
ClipCursor(nil);
FGrabIndex := -1;
FIsInSelfRect := False;
FOperateMouse := False;
end;
//=============================================================================
// WM_LBUTTONDBLCLKメッセージ処理
// 外部からOnDblclickイベントを定義して使用する場合の対応策
// マウスボタンUPと同じ処理をして描画コントロールを掴んでいない状態にする
//=============================================================================
procedure TplBaseDragResize.WMLButtonDblClk(var Message: TWMLButtonDblClk);
begin
inherited;
if FExitMouseEvent then exit;
if Assigned(OnDblClick) then begin
ClipCursor(nil);
FGrabIndex := -1;
FIsInSelfRect := False;
FOperateMouse := False;
end;
end;
//=============================================================================
// CM_HITTESTメッセージ処理
// FMousethroughはマウス透過のプロパティ
//=============================================================================
procedure TplBaseDragResize.CMHitTest(var Message: TCMHitTest);
begin
if (FMousethrough) or (FExitMouseEvent) then begin
Message.Result := HTNOWHERE;
end else begin
inherited;
end;
end;
//=============================================================================
// 描画コントロール(自分自身)の位置,サイズが変更になった場合
// ラバーバンドとグラブハンドルも再描画
//=============================================================================
procedure TplBaseDragResize.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
inherited;
if not Assigned(Parent) then exit;
if FUseExtend then begin
FExtendCtrl.SetBounds(Left - FAddExtend, Top - FAddExtend,
Width + FAddExtend * 2, Height + FAddExtend * 2);
end;
//
if (Top < Parent.ClientHeight) or ((Top + Height) > 0)
or ((Left + Width) < 0) or (Left > Parent.ClientWidth) then begin
SetRubberBandGrabHandlePosSize;
end;
if not FOperateMouse then begin
if (AWidth <= 0) or (AHeight <= 0) then begin
AWidth := Max(AWidth, 1);
AHeight := Max(AHeight, 1);
SetBounds(ALeft, ATop, AWidth, AHeight);
end;
end;
end;
//-----------------------------------------------------------------------------
// ラバーバンドとグラブハンドルを指定サイズと位置に描画
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetRubberBandGrabHandlePosSize;
var
LGrabNo : Integer;
begin
if Parent = nil then Exit;
if not FSelected then begin
FRubberBand.Visible := False;
for LGrabNo := 0 to Length(FGrabHandle) - 1 do begin
FGrabHandle[LGrabNo].Visible := False;
end;
exit;
end;
//ラバーバンド
FRubberBand.SetBounds(Left, Top, Width, Height);
FRubberBand.Visible := Visible and not FDispOnly;
//グラブハンドル
if not FMoveOnly then begin
RectToGrabHandlePoints(Bounds(Left, Top, Width, Height));
for LGrabNo := 0 to Length(FGrabHandle) - 1 do begin
FGrabHandle[LGrabNo].Left := FGrabPoints[LGrabNo].X;
FGrabHandle[LGrabNo].Top := FGrabPoints[LGrabNo].Y;
FGrabHandle[LGrabNo].Visible := Visible and not FDispOnly;
end;
if Visible and not FDispOnly then begin
if FGrabCornerOnly then begin
FGrabHandle[1].Visible := False;
FGrabHandle[3].Visible := False;
FGrabHandle[5].Visible := False;
FGrabHandle[7].Visible := False;
end else
if FGrabHorzOnly then begin
FGrabHandle[0].Visible := False;
FGrabHandle[1].Visible := False;
FGrabHandle[2].Visible := False;
FGrabHandle[4].Visible := False;
FGrabHandle[5].Visible := False;
FGrabHandle[6].Visible := False;
FGrabHandle[3].Invalidate;
FGrabHandle[7].Invalidate;
end else
if FGrabVertOnly then begin
FGrabHandle[0].Visible := False;
FGrabHandle[2].Visible := False;
FGrabHandle[3].Visible := False;
FGrabHandle[4].Visible := False;
FGrabHandle[6].Visible := False;
FGrabHandle[7].Visible := False;
FGrabHandle[1].Invalidate;
FGrabHandle[5].Invalidate;
end;
end;
end;
end;
//-----------------------------------------------------------------------------
// グラブハンドルの表示座標を計算
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.RectToGrabHandlePoints(ARect: TRect);
var
LHalfW : Integer;
LLeft : Integer;
LTop : Integer;
begin
LHalfW := FGrabHandleSize div 2;
LLeft := ARect.Left + (ARect.Right - ARect.Left) div 2;
LTop := ARect.Top + (ARect.Bottom - ARect.Top) div 2;
FGrabPoints[0] := Point(ARect.Left - LHalfW, ARect.Top - LHalfW);
FGrabPoints[1] := Point(LLeft - LHalfW, ARect.Top - LHalfW);
FGrabPoints[2] := Point(ARect.Right - LHalfW, ARect.Top - LHalfW);
FGrabPoints[3] := Point(ARect.Right - LHalfW, LTop - LHalfW);
FGrabPoints[4] := Point(ARect.Right - LHalfW, ARect.Bottom - LHalfW);
FGrabPoints[5] := Point(LLeft - LHalfW, ARect.Bottom - LHalfW);
FGrabPoints[6] := Point(ARect.Left - LHalfW, ARect.Bottom - LHalfW);
FGrabPoints[7] := Point(ARect.Left - LHalfW, LTop - LHalfW);
end;
//-----------------------------------------------------------------------------
// FDraw関係の値を元に戻す
// 以下の値をFalseにすれば描画の際に反転状態を元に戻すことができる
// 外部からTGPBitmapを取得した場合反転した状態の画像となる
// それを編集してこの描画コントロールで表示すると反転してしまう
// その対策用
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.ResetInverseDisp;
begin
FInverseHolz := False;
FInverseVert := False;
Invalidate;
end;
//-----------------------------------------------------------------------------
// 拡張描画領域用
// ExtendRectプロパティの取得用メソッド
// 実際に描画している範囲のTRect値を計算
//-----------------------------------------------------------------------------
function TplBaseDragResize.GetFExtendRect: TRect;
var
LGPBitmap : TGPBitmap;
begin
if not FUseExtend then begin
Result := Bounds(Left, Top, Width, Height);
Exit;
end;
LGPBitmap := TGPBitmap.Create(FExtendCtrl.Width, FExtendCtrl.Height);
try
DrawRoutine(nil, LGPBitmap);
Result := GetRealRect(LGPBitmap);
finally
FreeAndNil(LGPBitmap);
end;
end;
//-----------------------------------------------------------------------------
// GetFExtendRect メソッドで使用する関数
// 実際にデータがある TRect を取得
//-----------------------------------------------------------------------------
function TplBaseDragResize.GetRealRect(AGPBitmap: TGPBitmap): TRect;
var
LImgWidth : Integer;
LImgHeight : Integer;
LBmpData : TBitmapData;
LScanLine : PByte;
LpRGBQuad : PRGBQuad;
LDataStride : Integer;
LXPos : integer;
LYPos : Integer;
LAValue : Byte;
LXStartPos : Integer;
LXEndPos : Integer;
LYStartPos : Integer;
LYEndPos : Integer;
begin
LImgWidth := AGPBitmap.GetWidth;
LImgHeight := AGPBitmap.GetHeight;
LXStartPos := LImgWidth;
LXEndPos := 0;
LYStartPos := LImgHeight;
LYEndPos := 0;
//TGPBitmapのビットマップデータ取得
if AGPBitmap.LockBits(MakeRect(0, 0, LImgWidth, LImgHeight), ImageLockModeWrite,
PixelFormat32bppARGB, LBmpData) <> Ok then exit;
LScanLine := LBmpData.Scan0;
LDataStride := LBmpData.Stride;
for LYPos := 0 to LImgHeight - 1 do begin
LpRGBQuad := PRGBQuad(LScanLine);
for LXPos := 0 to LImgWidth - 1 do begin
LAValue := LpRGBQuad^.rgbReserved;
if LAValue <> 0 then begin
LXStartPos := Min(LXStartPos, LXPos);
LYStartPos := Min(LYStartPos, LYPos);
LXEndPos := Max(LXEndPos, LXPos);
LYEndPos := Max(LYEndPos, LYPos);
end;
Inc(LpRGBQuad);
end;
Inc(LScanLine, LDataStride);
end;
AGPBitmap.UnlockBits(LBmpData);
Result := Bounds(FExtendCtrl.Left + LXStartPos, FExtendCtrl.Top + LYStartPos,
LXEndPos - LXStartPos, LYEndPos - LYStartPos);
end;
//-----------------------------------------------------------------------------
// 拡張描画領域用
// ExtendLeft等のプロパティ取得用関数
// 実際の描画開始位置とサイズを取得
//
// 0 ExtendLeft
// 1 ExtendTop
// 2 ExtendRight
// 3 ExtendBottom
// 4 ExtendWidth
// 5 ExtendHeight
//-----------------------------------------------------------------------------
function TplBaseDragResize.GetExtendPosSize(const Value: Integer): Integer;
var
LRect : TRect;
begin
LRect := GetFExtendRect;
case Value of
0: Result := LRect.Left;
1: Result := LRect.Top;
2: Result := LRect.Right;
3: Result := LRect.Bottom;
4: Result := LRect.Right - LRect.Left;
5: Result := LRect.Bottom - LRect.Top;
else
Result := 0;
end;
end;
//-----------------------------------------------------------------------------
// 描画内容をTGPBitmap 画像として取得する時に使用するメソッド
// 戻り値の TGPBitmap は呼出し側で生成 / 解放する必要はない
//-----------------------------------------------------------------------------
function TplBaseDragResize.GetGPBitmap: TGPBitmap;
var
LGPBitmap : TGPBitmap;
begin
if FUseExtend then begin
LGPBitmap := TGPBitmap.Create(Width + AddExtend * 2, Height + AddExtend * 2);
end else begin
LGPBitmap := TGPBitmap.Create(Width, Height);
end;
try
DrawRoutine(nil, LGPBitmap);
Result := GetExtendGPbitmap(LGPBitmap);
finally
FreeAndNil(LGPBitmap);
end;
end;
//-----------------------------------------------------------------------------
// 与えられたビットマップの指定の色のアルファ値を0(透明色)にする関数
//
// ABitmap : 処理対象のビットマップ.処理したビットマップを返す
// TransColor : 透明にする色
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetColorToAlphaZero(ABitmap: TBitmap; TransColor: Cardinal);
type
TRGBQArray = array [0..4095] of RGBQUAD;
PRGBQArray = ^TRGBQArray;
var
LTransR : Byte;
LTransG : Byte;
LTransB : Byte;
LRValue : Byte;
LGValue : Byte;
LBValue : Byte;
LYPos : Integer;
LXPos : Integer;
LScanArray : PRGBQArray;
LColorQuad : RGBQUAD;
begin
ABitmap.Canvas.Lock;
ABitmap.PixelFormat := pf32bit;
//指定色のR,LGValue,Bの各値を取得しておく
LTransR := GetRValue(TransColor);
LTransG := GetGValue(TransColor);
LTransB := GetBValue(TransColor);
for LYPos := 0 to ABitmap.Height - 1 do begin
LScanArray := ABitmap.ScanLine[LYPos];
for LXPos := 0 to ABitmap.Width - 1 do begin
LRValue := LScanArray[LXPos].rgbRed;
LGValue := LScanArray[LXPos].rgbGreen;
LBValue := LScanArray[LXPos].rgbBlue;
if (LTransR = LRValue) and (LTransG = LGValue) and (LTransB = LBValue) then begin
LColorQuad.rgbBlue := 0;
LColorQuad.rgbGreen := 0;
LColorQuad.rgbRed := 0;
LColorQuad.rgbReserved := 0;
LScanArray[LXPos] := LColorQuad;
end else begin
LScanArray[LXPos].rgbReserved := $FF;
end;
end;
end;
ABitmap.Canvas.Unlock;
end;
//-----------------------------------------------------------------------------
// TBitmap を TGPBitmap に変換
// TGPBitmap は TBitmap のアルファチャンネルを処理しない
// TBitmapのアルファ値をTGPBitmapのアルファ値に代入する
// この処理ではアルファチャンネル付き TBitmapを TGPBitmap に変換する
//-----------------------------------------------------------------------------
function TplBaseDragResize.BitmapToGPBitmap(SrcBitmap: TBitmap; var DstGPBitmap: TGPBitmap;
PixelFmt: TPixelFormat): Boolean;
type
TRGBQArray = array [0..4095] of RGBQUAD;
PRGBQArray = ^TRGBQArray;
var
LBmpData : TBitmapData;
LScanLine : PByte;
LpRGBQuad : PRGBQuad;
LWidth : Integer;
LHeight : Integer;
LDataStride : Integer;
LXPos : integer;
LYPos : Integer;
LScanArray : PRGBQArray;
LAValue : Byte;
LRValue : Byte;
LGValue : Byte;
LBValue : Byte;
begin
Result := False;
LWidth := SrcBitmap.Width;
LHeight := SrcBitmap.Height;
FreeAndNil(DstGPBitmap);
DstGPBitmap := TGPBitmap.Create(LWidth, LHeight);
if DstGPBitmap.LockBits(MakeRect(0, 0, LWidth, LHeight), ImageLockModeWrite,
PixelFmt, LBmpData) <> Ok then exit;
LScanLine := LBmpData.Scan0;
LDataStride := LBmpData.Stride;
for LYPos := 0 to LHeight - 1 do begin
LpRGBQuad := PRGBQuad(LScanLine);
LScanArray := SrcBitmap.ScanLine[LYPos];
for LXPos := 0 to LWidth - 1 do begin
LAValue := LScanArray[LXPos].rgbReserved;
LRValue := LScanArray[LXPos].rgbRed;
LGValue := LScanArray[LXPos].rgbGreen;
LBValue := LScanArray[LXPos].rgbBlue;
if (RGB(LRValue, LGValue, LBValue) = 0) and (LAValue = 0) then begin
LpRGBQuad^.rgbReserved := 0;
end else
if (LAValue = 0) then begin
LpRGBQuad^.rgbReserved := $FF;
end else begin
LpRGBQuad^.rgbRed := LRValue;
LpRGBQuad^.rgbGreen := LGValue;
LpRGBQuad^.rgbBlue := LBValue;
LpRGBQuad^.rgbReserved := LScanArray[LXPos].rgbReserved;
end;
//色の塗り潰しの色指定が全てARGBで指定可能であればこの1行だけでよい
//LpRGBQuad^.rgbReserved := LScanArray[LXPos].rgbReserved;
Inc(LpRGBQuad);
end;
Inc(LScanLine, LDataStride);
end;
Result := True;
DstGPBitmap.UnlockBits(LBmpData);
end;
//-----------------------------------------------------------------------------
// TGPBitmap を TBitmap に変換
//-----------------------------------------------------------------------------
function TplBaseDragResize.GPBitmapToBitmap(SrcGPBitmap: TGPBitmap;
DstBitmap: TBitmap): Boolean;
var
LhBitmap : HBITMAP;
begin
DstBitmap.AlphaFormat := afDefined;
SrcGPBitmap.GetHBITMAP(0, LhBitmap);
DstBitmap.Handle := LhBitmap;
Result := True;
end;
//-----------------------------------------------------------------------------
// 拡張描画領域用
// 引数のTGPBitmap(描画されているTGPBitmap)から実際の画像部分だけを取得
// リージョンを作成してそのリージョンの位置から画像部分をコピーする
//-----------------------------------------------------------------------------
function TplBaseDragResize.GetExtendGPbitmap(AGPBitmap: TGPBitmap): TGPBitmap;
var
LRetGPBitmap : TGPBitmap;
LGPImageRgn : TGPRegion;
LGPGraphics : TGPGraphics;
LRgnRect : TGPRect;
begin
if not FUseExtend then begin
Result := TGPBitmap(AGPBitmap.Clone);
Exit;
end;
LGPImageRgn := TGPRegion.Create;
try
GetRegionFromGPBitmap(AGPBitmap, LGPImageRgn);
LGPGraphics := TGPGraphics.Create(Canvas.Handle);
LGPImageRgn.GetBounds(LRgnRect, LGPGraphics);
FreeAndNil(LGPGraphics);
LRetGPBitmap := TGPBitmap.Create(LRgnRect.Width, LRgnRect.Height);
LGPGraphics := TGPGraphics.Create(LRetGPBitmap);
LGPGraphics.DrawImage(AGPBitmap,
MakeRect(0, 0, LRgnRect.Width, LRgnRect.Height),
LRgnRect.X,
LRgnRect.Y,
LRgnRect.Width,
LRgnRect.Height,
UnitPixel);
Result := TGPBitmap(LRetGPBitmap.Clone);
finally
FreeAndNil(LGPGraphics);
FreeAndNil(LGPImageRgn);
FreeAndNil(LRetGPBitmap);
end;
end;
//-----------------------------------------------------------------------------
// リージョンの作成用関数
// 拡張描画領域用
// 透明色以外のピクセルをリージョンにする
// このリージョンを利用して実際の描画開始位置とサイズを取得する
//-----------------------------------------------------------------------------
function TplBaseDragResize.GetRegionFromGPBitmap(GDPBitmap: TGPBitmap;
GDPRegion: TGPRegion): Boolean;
var
LImgWidth : Integer;
LImgHeight : Integer;
LBmpData : TBitmapData;
LScanLine : PByte;
LpQuad : PRGBQuad;
LDataStride : Integer;
LXPos : integer;
LYPos : Integer;
LAValue : Byte;
LRgnRect : TGPRect;
LInImage : Boolean;
begin
Result := False;
GDPRegion.MakeEmpty;
LImgWidth := GDPBitmap.GetWidth;
LImgHeight := GDPBitmap.GetHeight;
//ビットマップとしてのデータを取得
LRgnRect := MakeRect(0, 0, 0, 0);
if GDPBitmap.LockBits(MakeRect(0, 0, LImgWidth, LImgHeight),ImageLockModeWrite,
PixelFormat32bppARGB, LBmpData) <> Ok then exit;
LScanLine := LBmpData.Scan0;
LDataStride := LBmpData.Stride;
LInImage := False;
//リージョンの領域矩形(ピクセル数の初期値は0
//透明色以外の時はそのピクセルを追加してUnionしていく
LRgnRect := MakeRect(0, 0, 0, 0);
for LYPos := 0 to LImgHeight - 1 do begin
LpQuad := PRGBQuad(LScanLine);
for LXPos := 0 to LImgWidth - 1 do begin
LAValue := LpQuad^.rgbReserved;
if not LInImage then begin
if LAValue <> 0 then begin
LInImage := True;
LRgnRect.X := LXPos;
LRgnRect.Y := LYPos;
LRgnRect.Height := 1;
end;
end else begin
if LAValue = 0 then begin
LInImage := False;
LRgnRect.Width := LXPos - LRgnRect.X;
GDPRegion.Union(LRgnRect);
end;
end;
Inc(LpQuad);
end;
if LInImage then begin
LInImage := False;
LRgnRect.Width := LImgWidth - LRgnRect.X;
GDPRegion.Union(LRgnRect);
end;
Inc(LScanLine, LDataStride);
end;
Result := True;
GDPBitmap.UnlockBits(LBmpData);
end;
//-----------------------------------------------------------------------------
// GDI+ を使用しない DrawRoutine で使用するメソッド
// 描画先の TBitmap とその TCanvas を設定
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.SetBitmapCanvas_forDraw(ABitmap: TBitmap; var ACanvas: TCanvas;
ATransColor: TColor);
begin
ABitmap.SetSize(Width + FAddExtend * 2, Height + FAddExtend * 2);
ABitmap.PixelFormat := pf32bit;
ABitmap.AlphaFormat := afIgnored;
ACanvas := ABitmap.Canvas;
ACanvas.Brush.Color := ATransColor;
ACanvas.FillRect(ACanvas.ClipRect);
end;
//-----------------------------------------------------------------------------
// GDI+ を使用しない DrawRoutine で使用するメソッド
// 描画を AGPBitmap (TGPBitmap) に返す処理
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.BitmapToGPBitmap_forDraw(ABitmap: TBitmap; AGPBitmap: TGPBitmap;
ATransColor: TColor; AOffsetX: Integer; AOffsetY: Integer);
var
LTempGPBmp : TGPBitmap;
LGPGraphic : TGPGraphics;
LWidth : Integer;
LHeight : Integer;
begin
LWidth := ABitmap.Width;
LHeight := ABitmap.Height;
LTempGPBmp := TGPBitmap.Create(LWidth, LHeight);
try
// ここで透過色を透明に変えてアルファチャンネル付きの TBitmap にする
SetColorToAlphaZero(ABitmap, ATransColor);
ABitmap.AlphaFormat := afDefined;
// TBitmap を TGPBitmap に変換
BitmapToGPBitmap(ABitmap, LTempGPBmp, PixelFormat32bppARGB);
// 描画先を AGPBitmap にした TGPGraphics に LTempGPBmp を描画
if FUseExtend then begin
LGPGraphic := TGPGraphics.Create(AGPBitmap);
LGPGraphic.DrawImage(LTempGPBmp, AOffsetX, AOffsetY);
end else begin
LGPGraphic := TGPGraphics.Create(AGPBitmap);
LGPGraphic.DrawImage(LTempGPBmp, AOffsetX, AOffsetY);
end;
finally
FreeAndNil(LTempGPBmp);
FreeAndNil(LGPGraphic);
end;
end;
//-----------------------------------------------------------------------------
// 実際の描画用の処理
// 引数のACanvasにこのメソッド内の描画コードを実行する
// 実際の描画コードは派生クラスに継承メソッドを実装して行う
//
// 描画開始位置は常に(FDrawLeft, FDrawTop)
// 反転した描画が不要なら Rect(0, 0, Widht, Heidht) 内の描画でよい
//-----------------------------------------------------------------------------
procedure TplBaseDragResize.DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap;
AOffsetX: Integer; AOffsetY: Integer);
begin
if (ACanvas = nil) and (AGPBitmap = nil) then exit;
//上下左右が反転の時
if FInverseDisp and FInverseHolz and FInverseVert then begin
//左右のX座標値(Left,Right)を交換する
//上下のY座標値(Top,Bottom)を交換する
FDrawLeft := ClientRect.Right + AOffsetX;
FDrawTop := ClientRect.Bottom + AOffsetY;
FDrawRight := ClientRect.Left + AOffsetX;
FDrawBottom := ClientRect.Top + AOffsetY;
FDrawWidth := -Width;
FDrawHeight := -Height;
end else
//左右が反転の時
if FInverseDisp and FInverseHolz then begin
//左右のX座標値(Left,Right)を交換する
FDrawLeft := ClientRect.Right + AOffsetX;
FDrawTop := ClientRect.Top + AOffsetY;
FDrawRight := ClientRect.Left + AOffsetX;
FDrawBottom := ClientRect.Bottom + AOffsetY;
FDrawWidth := -Width;
FDrawHeight := Height;
end else
//上下が反転の時
if FInverseDisp and FInverseVert then begin
//上下のY座標値(Top,Bottom)を交換する
FDrawLeft := ClientRect.Left + AOffsetX;
FDrawTop := ClientRect.Bottom + AOffsetY;
FDrawRight := ClientRect.Right + AOffsetX;
FDrawBottom := ClientRect.Top + AOffsetY;
FDrawWidth := Width;
FDrawHeight := -Height;
end else begin
FDrawLeft := ClientRect.Left + AOffsetX;
FDrawTop := ClientRect.Top + AOffsetY;
FDrawRight := ClientRect.Right + AOffsetX;
FDrawBottom := ClientRect.Bottom + AOffsetY;
FDrawWidth := Width;
FDrawHeight := Height;
end;
FDrawRect := Bounds(FDrawLeft, FDrawTop, FDrawWidth, FDrawHeight);
FSignX := Sign(FDrawWidth);
FSignY := Sign(FDrawHeight);
end;
//=============================================================================
// Invalidate メソッド
//=============================================================================
procedure TplBaseDragResize.Invalidate;
begin
if (FUseExtend and Assigned(FExtendCtrl)) then begin
FExtendCtrl.Invalidate;
end;
inherited;
end;
//=============================================================================
// Paintメソッド処理
// 実際の描画処理は継承クラスのDrawRoutineメソッドで行う
//=============================================================================
procedure TplBaseDragResize.Paint;
begin
inherited;
if not Assigned(Parent) then exit;
//幅や高さが負になった場合の処理
if Width < 0 then begin
FInverseHolz := not FInverseHolz;
Width := Abs(Width);
Left := Left - Width;
if FSelected then begin
case FGrabIndex of
0: FGrabIndex := 2;
7: FGrabIndex := 3;
6: FGrabIndex := 4;
2: FGrabIndex := 0;
3: FGrabIndex := 7;
4: FGrabIndex := 6;
end;
end;
end;
if Height < 0 then begin
FInverseVert := not FInverseVert;
Height := Abs(Height);
Top := Top - Abs(Height);
if FSelected then begin
case FGrabIndex of
0: FGrabIndex := 6;
1: FGrabIndex := 5;
2: FGrabIndex := 4;
4: FGrabIndex := 2;
5: FGrabIndex := 1;
6: FGrabIndex := 0;
end;
end;
end;
//描画ルーチンの実行
if Assigned(FExtendCtrl) then begin
DrawRoutine(FExtendCtrl.Canvas);
end else begin
DrawRoutine(Canvas);
end;
// ラバーバンドの位置とサイズ設定
SetBounds(Left, Top, Width, Height);
end;
{ TplBaseGrabHandle }
//=============================================================================
// グラブハンドルクラスのコンストラクタ
// 基本的な設定のみ
//=============================================================================
constructor TplBaseGrabHandle.Create(AOwner: TComponent);
begin
inherited;
FResizeBase := TplBaseDragResize(AOwner);
Brush.Style := bsSolid;
Brush.Color := clWhite;
Pen.Color := clBlack;
Pen.Width := 2;
Pen.Style := psSolid;
end;
//=============================================================================
// CM_HITTESTメッセージ処理
// TplBaseDragResizeがマウス透過時はグラブハンドルもマウスイベント透過とする
//=============================================================================
procedure TplBaseGrabHandle.CMHitTest(var Message: TCMHitTest);
begin
if(FResizeBase.Mousethrough) or (FResizeBase.FExitMouseEvent) then begin
Message.Result := HTNOWHERE;
end else begin
inherited;
end;
end;
//=============================================================================
// MouseDownメソッド処理
// TplBaseDragResizeで処理する
//=============================================================================
procedure TplBaseGrabHandle.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
var
LPos : TPoint;
begin
inherited;
if FResizeBase.Selected then begin
LPos := ClientToParent(Point(X, Y));
FResizeBase.MouseDown(Button, Shift, LPos.X, LPos.Y);
end;
end;
//=============================================================================
// MouseMoveメソッド処理
// TplBaseDragResizeで処理する
//=============================================================================
procedure TplBaseGrabHandle.MouseMove(Shift: TShiftState; X, Y: Integer);
var
LPos : TPoint;
begin
inherited;
if FResizeBase.Selected then begin
LPos := ClientToParent(Point(X, Y));
FResizeBase.MouseMove(Shift, LPos.X, LPos.Y);
end;
end;
//=============================================================================
// MouseUpメソッド処理
// TplBaseDragResizeで処理する
//=============================================================================
procedure TplBaseGrabHandle.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
var
LPos : TPoint;
begin
inherited;
if FResizeBase.Selected then begin
LPos := ClientToParent(Point(X, Y));
FResizeBase.MouseUp(Button, Shift, LPos.X, LPos.Y);
end;
end;
{ TplBaseRubberBand }
//=============================================================================
// ラバーバンドクラスのコンストラクタ
// 基本的な設定のみ
//=============================================================================
constructor TplBaseRubberBand.Create(AOwner: TComponent);
begin
inherited;
Brush.Style := bsClear;
Pen.Color := clBlack;
Pen.Width := 1;
Pen.Style := psDot;
end;
//=============================================================================
// CM_HITTESTメッセージ処理
// マウスイベントは全て透過
// マウスイベントは全てTplBaseDragResizeで処理する
//=============================================================================
procedure TplBaseRubberBand.CMHitTest(var Message: TCMHitTest);
begin
Message.Result := HTNOWHERE;
end;
{ TplBaseExtendCtrl }
//=============================================================================
// 拡張描画用コントロール
// コンストラクタ
//=============================================================================
constructor TplBaseExtendCtrl.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ParentColor := False;
Canvas.Brush.Style := bsClear;
FBaseCtrl := TplBaseDragResize(AOwner);
FBaseCtrl.ExtendCtrl := Self;
FBaseCtrl.UseExtend := True;
end;
//=============================================================================
// 拡張描画用コントロール
// CM_HITTESTメッセージ処理
// マウスイベントは全て透過とする
//=============================================================================
procedure TplBaseExtendCtrl.CMHitTest(var Message: TCMHitTest);
begin
Message.Result := HTNOWHERE;
end;
//=============================================================================
// 拡張描画用コントロール
// Paint メソッド
// ここで描画ルーチンを実行する
//=============================================================================
procedure TplBaseExtendCtrl.Paint;
begin
inherited;
FBaseCtrl.DrawRoutine(Canvas);
end;
end.
|