|
画像・図形の移動リサイズ関係サンプル
R040_直線を描画するクラスの例 |
動作確認等 |
Windows 7 U64(SP1) + Delphi XE(UP1) Pro |
|
R040_ResizeLine.zip [1,878 KB] 2017年09月20日版 (EXE 同梱) |
矢印付きの直線を描画するコントロールのクラスです.
この直線描画用のクラスは特別な工夫をしています.
下図は基本クラスのページで説明した直線描画のコードで,線の幅を大きくした時の状態を示しています.直線の端点が描画コントロールでクリップされて描画が欠落しています.また,水平または垂直に近くなるほど線の幅が小さくなってしまいます. |
|
図1
直線描画の問題
|
|
図2
垂直に近い状態にした場合
|
この現象を回避するため,本来の描画コントロールとは別の少し大きい TGrapicControl を使用して,そこに実際の直線を描画するようにしています.
以下のプロパティを実装してます.追加のメソッドはありません. |
プロパティ |
型 |
説明 |
ExtendRect |
TRect |
実際の描画領域の TRect の値.読み出し専用 |
ExtendLeft |
Integer |
実際の描画領域の左端の X 座標値.読み出し専用 |
ExtendTop |
Integer |
実際の描画領域の上端の Y 座標値.読み出し専用 |
ExtendRight |
Integer |
実際の描画領域の右端の X 座標値.読み出し専用^ |
ExtendBottom |
Integer |
実際の描画領域の下端の Y 座標値.読み出し専用 |
ExtendWidth |
Integer |
実際の描画領域の幅.読み出し専用 |
ExtendHeight |
Integer |
実際の描画領域の高さ.読み出し専用 |
LineType |
TplLineType |
水平な直線,垂直な直線,斜めの直線の指定
初期値は ltNormal で斜めの直線 |
LineStartPos |
TPoint |
描画開始の座標値.描画コントロールの生成時のみ有効な設定
指定しない場合は描画コントロールの左上の座標値 |
LineEndPos |
TPoint |
描画終点の座標値.描画コントロールの生成時のみ有効な設定
指定しない場合は描画コントロールの右下の座標値 |
PenWidth |
Single |
線幅.初期値は 3.0 |
PenColor |
TGPColor |
線の色.GDI+ での色での指定なので注意.初期値は aclBlack (黒) |
MiddleInset |
Single |
矢印の底辺の切り込み深さ.線幅の倍数で指定.初期値は 0.0 |
ArrowWidth |
Single |
矢印の幅.線幅の倍数で指定.初期値は 3.3 |
ArrowHeight |
Single |
矢印の高さ.線幅の倍数で指定.初期値は 4.5 |
StartArrow |
Boolean |
描画開始点の矢印描画の有無.初期値は False |
EndArrow |
Boolean |
描画終点の矢印描画の有無.初期値は False |
作成した直線描画コントロールのクラスのテストです.
直線の端点が欠落しないで描画できるこど確認できます.垂直に近い状態になっても線幅が保たれていることも確認できます.
|
|
図3
実行して直線を描画 |
|
図4
垂直に近い状態にした場合 |
|
図5
矢印付の直線にした場合 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls, plBaseDragResize, plGPResizeLine, Spin, plContainerPanel,
GDIPAPI, GDIPOBJ, GDIPUTIL;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
SpinEdit1: TSpinEdit;
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
ColorDialog1: TColorDialog;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure SpinEdit1Change(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
private
{ Private 宣言 }
FBackCtrl : TplContainerPanel;
FResizeLine : TplGPResizeLine;
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;
SpinEdit1.MinValue := 1;
SpinEdit1.MaxValue := 200;
SpinEdit1.Value := SpinEdit1.MinValue;
end;
//=============================================================================
// [生成]ボタン
// GDI+ の色は ARGBのバイト順
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
FBackCtrl.DeleteAllCtrls;
FResizeLine := TplGPResizeLine.Create(Self);
FResizeLine.Parent := FBackCtrl;
FResizeLine.InverseDisp := True;
FResizeLine.PenWidth := 10.0;
FResizeLine.PenColor := $FF00A0FF;
SpinEdit1.Value := Trunc(FResizeLine.PenWidth);
ColorDialog1.Color := ARGBToColorRef(FResizeLine.PenColor);
end;
//=============================================================================
// 線の色を変更
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
if not Assigned(FResizeLine) then exit;
if ColorDialog1.Execute then begin
FResizeLine.PenColor := ColorRefToARGB(ColorDialog1.Color);
end;
end;
//=============================================================================
// 線の太さを変更
//=============================================================================
procedure TForm1.SpinEdit1Change(Sender: TObject);
begin
if not Assigned(FResizeLine) then exit;
FResizeLine.PenWidth := SpinEdit1.Value;
end;
//=============================================================================
// 矢印の描画有無の設定
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if not Assigned(FResizeLine) then exit;
FResizeLine.StartArrow := CheckBox1.Checked;
FResizeLine.EndArrow := CheckBox2.Checked;
TCheckBox(Sender).Invalidate;
end;
end.
描画コントロールの高さを 0 にすれば水平な直線,幅を 0 にすれば垂直な直線になりますが,提供している描画コントロールでは幅または高さを 0 にできないようにしています.
幅または高さを 0 にしてしまうと,描画コントロールのサイズが 0 になってしまい,選択できなくなってしまいます.サイズが 0 の存在しないコントロールは選択できません.
そこで,水平な直線と垂直な直線はプロパティで指定するようにしました.以下の列挙型のいすれかの値で指定します.
下図はそれらの値を使用して実際に水平な,あるいは垂直な直線を描画するテストの結果です.
|
TplGPResizeLine.LineType: TplLineType
TplLineType = (ltNormal, ltHorz, ltVert)
|
図6
実行開始時は垂直な直線を描画 |
|
図7
斜めの直線に変更
|
|
図8
水平な直線に変更
|
|
図9
垂直な直線にして矢印を追加
|
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls, plBaseDragResize, plGPResizeLine, Spin, plContainerPanel,
GDIPAPI, GDIPOBJ, GDIPUTIL;
type
TForm1 = class(TForm)
Panel1: TPanel;
Button1: TButton;
Button2: TButton;
SpinEdit1: TSpinEdit;
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
ColorDialog1: TColorDialog;
Button3: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure SpinEdit1Change(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
FBackCtrl : TplContainerPanel;
FResizeLine : TplGPResizeLine;
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;
SpinEdit1.MinValue := 1;
SpinEdit1.MaxValue := 200;
SpinEdit1.Value := SpinEdit1.MinValue;
end;
//=============================================================================
// [生成]ボタン
// GDI+ の色は ARGBのバイト順
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
FBackCtrl.DeleteAllCtrls;
FResizeLine := TplGPResizeLine.Create(Self);
FResizeLine.Parent := FBackCtrl;
FResizeLine.InverseDisp := True;
FResizeLine.PenWidth := 10.0;
FResizeLine.PenColor := $FF00A0FF;
// FResizeLine.LineStartPos := Point(250, 50);
// FResizeLine.LineEndPos := Point(100, 150);
FResizeLine.LineType := ltVert;
SpinEdit1.Value := Trunc(FResizeLine.PenWidth);
ColorDialog1.Color := ARGBToColorRef(FResizeLine.PenColor);
end;
//=============================================================================
// 線の色を変更
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
if not Assigned(FResizeLine) then exit;
if ColorDialog1.Execute then begin
FResizeLine.PenColor := ColorRefToARGB(ColorDialog1.Color);
end;
end;
//=============================================================================
// 線の太さを変更
//=============================================================================
procedure TForm1.SpinEdit1Change(Sender: TObject);
begin
if not Assigned(FResizeLine) then exit;
FResizeLine.PenWidth := SpinEdit1.Value;
end;
//=============================================================================
// 矢印の描画有無の設定
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
if not Assigned(FResizeLine) then exit;
FResizeLine.StartArrow := CheckBox1.Checked;
FResizeLine.EndArrow := CheckBox2.Checked;
TCheckBox(Sender).Update;
end;
//=============================================================================
// LineTypeプロパティの設定
// 水平な直線,垂直な直線,斜めの直線の切り換え,変更
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
LWidth : Integer;
LHeight : Integer;
begin
if not Assigned(FResizeLine) then exit;
LWidth := FResizeLine.Width;
LHeight := FResizeLine.Height;
if FResizeLine.LineType = ltNormal then begin
FResizeLine.LineType := ltHorz;
end else
if FResizeLine.LineType = ltHorz then begin
FResizeLine.LineType := ltVert;
FResizeLine.Width := LHeight;
FResizeLine.Height := LWidth;
end else
if FResizeLine.LineType = ltVert then begin
FResizeLine.LineType := ltNormal;
FResizeLine.Width := LHeight;
FResizeLine.Height := LWidth;
end;
end;
end.
03_描画を TBitmap で取得 - コントロールと実描画サイズ |
描画の状態を TBitmap のビットマップ画像として取得するサンプルです.
まず画像描画用コントロールの記事で説明したように,DrawRoutine で TGPBitmap の画像オブジェクトを取得します.この時,あらかじめ TGPBitmap を作成しておきますが,このサイズは描画コントロールのサイズではなく,実際の描画内容の幅と高さである以下のプロパティを使用します.
TGPBitmap の画像が取得できたら GetHBITMAP メソッドでビットマップのハンドルを取得します.
描画内容がアルファチャンネル付きの画像の時は,この GetHBITMAP を使用すると正常に取得できないことがありますが,直線描画用のクラスで描画した内容であれば問題ありません.
このテストでは,取得した TBitmap を TImage に描画します.
|
TplGPResizeLine.ExtendWidth
TplGPResizeLine.ExtendHeight
TGPBitmap.GetHBITMAP
|
図10
描画内容をビットマップに変換
- 右の TImage に表示しているのが取得したビットマップ画像
|
//=============================================================================
// TGPBitmapからTBitmapを作成
//
// 描画中のTGPBitmapを取得してTGPBitmap.GetHBITMAPメソッドで取得する方法
//
// ExtendWidth, ExtendHeightは実際の描画領域の幅と高さ
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
LCtrl : TplGPResizeLine;
LGPBitmap : TGPBitmap;
LhBitmap : HBITMAP;
LBitmap : TBitmap;
begin
if FBackCtrl.CtrlList.Count = 0 then exit;
Image1.Picture.Assign(nil);
LCtrl := TplGPResizeLine(FBackCtrl.CtrlList[0]);
LGPBitmap := TGPBitmap.Create(LCtrl.ExtendWidth, LCtrl.ExtendHeight);
LCtrl.DrawRoutine(nil, LGPBitmap);
LBitmap := TBitmap.Create;
try
LBitmap.AlphaFormat := afDefined;
LGPBitmap.GetHBITMAP(0, LhBitmap);
LBitmap.Handle := LhBitmap;
Image1.Picture.Bitmap.Assign(LBitmap);
finally
FreeAndNil(LBitmap);
FreeAndNil(LGPBitmap);
end;
end;
矢印付きの直線描画用コントロールクラスのソースコードです. |
リスト4
矢印付きの直線描画用コントロールクラスのソースコード
このユニットファイルをライブラリのパスが通った場所に置いて使用する |
unit plGPResizeLine;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, plBaseDragResize,
GDIPAPI, GDIPOBJ, GDIPUTIL;
type
TplGPResizeLine = class;
TplExtendResizeLine = class(TGraphicControl)
private
FLineCtrl : TplGPResizeLine;
protected
procedure CMHitTest(var Message: TCMHitTest); message CM_HITTEST;
procedure Paint; override;
public
constructor Create(AOwner: TComponent); override;
end;
TplLineType = (ltNormal, ltHorz, ltVert);
TplGPResizeLine = class(TplBaseDragResize)
private
FPenWidth : Single;
FPenColor : TGPColor;
FGPPath : TGPGraphicsPath;
FGPPen : TGPPen;
FExtendCtrl : TplExtendResizeLine;
FExtendHorz : Integer;
FExtendVert : Integer;
FLineStartPos : TPoint;
FLineEndPos : TPoint;
FArrowWidth : Single;
FLineType : TplLineType;
FMiddleInset : Single;
FArrowHeight : Single;
FStartArrow : Boolean;
FEndArrow : Boolean;
FCanDrawFlag : Boolean;
function GetRegionFromGPBitmap(GDPBitmap: TGPBitmap; GDPRegion: TGPRegion):
Boolean;
function GetExtendGPbitmap(AGPBitmap: TGPBitmap): TGPBitmap;
procedure SetFPenColor(const Value: TGPColor);
procedure SetFPenWidth(const Value: Single);
procedure SetDrawArrow(const Index: Integer; const Value: Boolean);
function GetGPBitmap: TGPBitmap;
function GetFExtendRect: TRect;
procedure SetFArrowHeight(const Value: Single);
procedure SetFArrowWidth(const Value: Single);
function GetExtendPosSize(const Value: Integer): Integer;
procedure SetFLineStartPos(const Value: TPoint);
procedure SetFLineEndPos(const Value: TPoint);
procedure SetFLineType(const Value: TplLineType);
procedure SetFMiddleInset(const Value: Single);
protected
FGetFlag : Boolean;
procedure SetParent(AParent: TWinControl); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure SetBounds(ALeft: Integer; ATop: Integer; AWidth: Integer;
AHeight: Integer); override;
procedure DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap = nil;
AOffsetX: Integer = 0; AOffsetY: Integer = 0); override;
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 LineType : TplLineType read FLineType write SetFLineType;
property PenWidth : Single read FPenWidth write SetFPenWidth;
property PenColor : TGPColor read FPenColor write SetFPenColor;
property LineStartPos : TPoint read FLineStartPos write SetFLineStartPos;
property LineEndPos : TPoint read FLineEndPos write SetFLineEndPos;
property MiddleInset : Single read FMiddleInset write SetFMiddleInset;
property ArrowWidth : Single read FArrowWidth write SetFArrowWidth;
property ArrowHeight : Single read FArrowHeight write SetFArrowHeight;
property StartArrow : Boolean index 0 read FStartArrow write SetDrawArrow;
property EndArrow : Boolean index 1 read FEndArrow write SetDrawArrow;
end;
implementation
uses
Types,
Math;
{ TplGPResizeLine }
//=============================================================================
// コンストラクタ
//=============================================================================
constructor TplGPResizeLine.Create(AOwner: TComponent);
begin
inherited;
FPenWidth := 3.0;
FPenColor := aclBlack;
FLineStartPos := Point(Left, Top);
FLineEndPos := Point(Left + Width, Top + Height);
FLineType := ltNormal;
FMiddleInset := 0;
FArrowWidth := 3.3;
FArrowHeight := 4.5;
FStartArrow := False;
FEndArrow := False;
FExtendHorz := 150;
FExtendVert := 150;
FCanDrawFlag := True;
end;
//=============================================================================
// デコンストラクタ処理
//=============================================================================
destructor TplGPResizeLine.Destroy;
begin
if Assigned(FGPPath) then FreeAndNil(FGPPath);
if Assigned(FGPPen) then FreeAndNil(FGPPen);
inherited;
end;
//=============================================================================
// SetParentメソッド処理
//=============================================================================
procedure TplGPResizeLine.SetParent(AParent: TWinControl);
begin
inherited;
if not Assigned(AParent) then exit;
if Assigned(FExtendCtrl) then exit;
FExtendCtrl := TplExtendResizeLine.Create(Self);
FExtendCtrl.Parent := Self.Parent;
BringToFront(FExtendCtrl);
end;
//=============================================================================
// SetBoundsメソッド処理
// 水平直線または垂直直線の時は幅または高さを0にできないようにする
//=============================================================================
procedure TplGPResizeLine.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
if (AWidth <> 0) and (AHeight <> 0) then begin
inherited;
end else begin
if (FLineType <> ltHorz) and (FLineType <> ltVert) then begin
inherited;
end;
end;
end;
//-----------------------------------------------------------------------------
// PenColorプロパティ設定用のメソッド
//-----------------------------------------------------------------------------
procedure TplGPResizeLine.SetFPenColor(const Value: TGPColor);
begin
if FPenColor <> Value then begin
FPenColor := Value;
FExtendCtrl.Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// PenWidthプロパティ設定用のメソッド
//-----------------------------------------------------------------------------
procedure TplGPResizeLine.SetFPenWidth(const Value: Single);
begin
if Value < 1.0 then exit;
if FPenWidth <> Value then begin
FPenWidth := Value;
if FLineType = ltHorz then begin
if FStartArrow or FEndArrow then begin
Height := Max(Trunc(FPenWidth) + 3, Trunc(FPenWidth * FArrowWidth) + 3);
end else begin
Height := Trunc(FPenWidth) + 3;
end;
end else
if FLineType = ltVert then begin
if FStartArrow or FEndArrow then begin
Width := Max(Trunc(FPenWidth) + 3, Trunc(FPenWidth * FArrowWidth) + 3);
end else begin
Width := Trunc(FPenWidth) + 3;
end;
end;
FExtendCtrl.Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// LineStartPosプロパティの設定用メソッド
// 描画開始位置を設定
// この描画コントロールの親であるコンテナコントロール上の位置の設定と同じ
//
// このプロパティが有効なのは描画コントロールの生成時のみ
//-----------------------------------------------------------------------------
procedure TplGPResizeLine.SetFLineStartPos(const Value: TPoint);
var
LLeft : Integer;
LLTop : Integer;
LLWidth : Integer;
LLHeight : Integer;
begin
if Value.X <= 0 then exit;
if Value.Y <= 0 then exit;
if (FLineStartPos.X <> Value.X) or (FLineStartPos.Y <> Value.Y) then begin
FLineStartPos := Value;
LLeft := FLineStartPos.X;
LLTop := FLineStartPos.Y;
FLineType:= ltNormal;
if FLineEndPos.X = 0 then FLineEndPos.X := FLineStartPos.X + Width;
if FLineEndPos.Y = 0 then FLineEndPos.Y := FLineStartPos.Y + Height;
LLWidth := FLineEndPos.X - FLineStartPos.X;
LLHeight := FLineEndPos.Y - FLineStartPos.Y;
SetBounds(LLeft, LLTop, Abs(LLWidth), Abs(LLHeight));
end;
end;
//-----------------------------------------------------------------------------
// LineEndPosプロパティの設定用メソッド
// 描画終了位置を設定
// この描画コントロールの親であるコンテナコントロール上の位置の設定と同じ
//
// このプロパティが有効なのは描画コントロールの生成時のみ
//-----------------------------------------------------------------------------
procedure TplGPResizeLine.SetFLineEndPos(const Value: TPoint);
var
LLeft : Integer;
LLTop : Integer;
LLWidth : Integer;
LLHeight : Integer;
begin
if Value.X <= 0 then exit;
if Value.Y <= 0 then exit;
FLineType:= ltNormal;
if (FLineEndPos.X <> Value.X) or (FLineEndPos.Y <> Value.Y) then begin
FLineEndPos := Value;
if FLineStartPos.X = 0 then FLineStartPos.X := FLineEndPos.X - Width;
if FLineStartPos.Y = 0 then FLineStartPos.Y := FLineEndPos.Y - Height;
LLeft := FLineStartPos.X;
LLTop := FLineStartPos.Y;
LLWidth := FLineEndPos.X - FLineStartPos.X;
LLHeight := FLineEndPos.Y - FLineStartPos.Y;
SetBounds(LLeft, LLTop, Abs(LLWidth), Abs(LLHeight));
end;
end;
//-----------------------------------------------------------------------------
// FLineTypeプロパティの設定用メソッド
// ltNormal 水平でも垂直でもない直線.斜めの直線
// ltHorz 水平な直線
// ltVert 垂直な直線
//-----------------------------------------------------------------------------
procedure TplGPResizeLine.SetFLineType(const Value: TplLineType);
begin
if FLineType <> Value then begin
FLineType := Value;
if FLineType = ltHorz then begin
if FStartArrow or FEndArrow then begin
Height := Max(Trunc(FPenWidth) + 3, Trunc(FPenWidth * FArrowWidth) + 3);
end else begin
Height := Trunc(FPenWidth) + 3;
end;
GrabHorzOnly := True;
end else
if FLineType = ltVert then begin
if FStartArrow or FEndArrow then begin
Width := Max(Trunc(FPenWidth) + 3, Trunc(FPenWidth * FArrowWidth) + 3);
end else begin
Width := Trunc(FPenWidth) + 3;
end;
GrabVertOnly := True;
end else begin
GrabHorzOnly := False;
GrabVertOnly := False;
end;
Invalidate;
FExtendCtrl.Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// MiddleInsetプロパティ(矢印のインセット)設定用メソッド
// 矢印の三角形の底辺の切り込み深さで線幅の倍数で指定
//-----------------------------------------------------------------------------
procedure TplGPResizeLine.SetFMiddleInset(const Value: Single);
begin
if FMiddleInset <> Value then begin
FMiddleInset := Value;
FExtendCtrl.Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// ArrowHeightプロパティ(矢印の高さ)設定用メソッド
// 矢印の三角形の高さで線幅の倍数で指定
//-----------------------------------------------------------------------------
procedure TplGPResizeLine.SetFArrowHeight(const Value: Single);
begin
if FArrowHeight <> Value then begin
FArrowHeight := Value;
FExtendCtrl.Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// ArrowWidthプロパティ(矢印幅)設定用メソッド
// 矢印の三角形底辺の長さで線幅の倍数で指定
//-----------------------------------------------------------------------------
procedure TplGPResizeLine.SetFArrowWidth(const Value: Single);
begin
if FArrowWidth <> Value then begin
FArrowWidth := Value;
if FLineType = ltHorz then begin
Height := Max(Trunc(FPenWidth) + 3, Trunc(FPenWidth * FArrowWidth) + 3);
end else
if FLineType = ltVert then begin
Width := Max(Trunc(FPenWidth) + 3, Trunc(FPenWidth * FArrowWidth) + 3);
end;
FExtendCtrl.Invalidate;
end;
end;
//-----------------------------------------------------------------------------
// 矢印の表示有無関係プロパティ設定用のメソッド
//-----------------------------------------------------------------------------
procedure TplGPResizeLine.SetDrawArrow(const Index: Integer;
const Value: Boolean);
begin
case Index of
0:
begin
if FStartArrow <> Value then begin
FStartArrow := Value;
end;
end;
1:
begin
if FEndArrow <> Value then begin
FEndArrow := Value;
end;
end;
end;
if FLineType = ltHorz then begin
if FStartArrow or FEndArrow then begin
Height := Max(Trunc(FPenWidth) + 3, Trunc(FPenWidth * FArrowWidth) + 3);
end else begin
Height := Trunc(FPenWidth) + 3;
end;
end else
if FLineType = ltVert then begin
if FStartArrow or FEndArrow then begin
Width := Max(Trunc(FPenWidth) + 3, Trunc(FPenWidth * FArrowWidth) + 3);
end else begin
Width := Trunc(FPenWidth) + 3;
end;
end;
FExtendCtrl.Invalidate;
end;
//-----------------------------------------------------------------------------
// ExtendRectプロパティの取得用メソッド
// 実際に描画している範囲のTRect値を計算
// 描画のリージョンを取得してリージョンの位置とサイズから取得
//-----------------------------------------------------------------------------
function TplGPResizeLine.GetFExtendRect: TRect;
var
LGPBitmap : TGPBitmap;
LGPImageRgn : TGPRegion;
LGPGraphics : TGPGraphics;
LRgnRect : TGPRect;
begin
Result := Bounds(0, 0, 0, 0);
LGPBitmap := TGPBitmap.Create(FExtendCtrl.Width, FExtendCtrl.Height);
FCanDrawFlag := False;
DrawRoutine(nil, LGPBitmap);
FCanDrawFlag := True;
LGPImageRgn := TGPRegion.Create;
try
if GetRegionFromGPBitmap(LGPBitmap, LGPImageRgn) then begin
LGPGraphics := TGPGraphics.Create(Canvas.Handle);
LGPImageRgn.GetBounds(LRgnRect, LGPGraphics);
Result := Bounds(FExtendCtrl.Left + LRgnRect.X, FExtendCtrl.Top + LRgnRect.Y,
LRgnRect.Width, LRgnRect.Height);
FreeAndNil(LGPGraphics);
end;
finally
FreeAndNil(LGPBitmap);
FreeAndNil(LGPImageRgn);
end;
end;
//-----------------------------------------------------------------------------
// ExtendLeft等のプロパティ取得用関数
// 実際の描画開始位置とサイズを取得
//-----------------------------------------------------------------------------
function TplGPResizeLine.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 TplGPResizeLine.GetExtendGPbitmap(AGPBitmap: TGPBitmap): TGPBitmap;
var
LRetGPBitmap : TGPBitmap;
LGPImageRgn : TGPRegion;
LGPGraphics : TGPGraphics;
LRgnRect : TGPRect;
begin
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(AGPBitmap);
FreeAndNil(LRetGPBitmap);
end;
end;
//-----------------------------------------------------------------------------
// 描画内容をTGPBitmap画像として取得する時に使用する
//-----------------------------------------------------------------------------
function TplGPResizeLine.GetGPBitmap: TGPBitmap;
var
LGPBitmap : TGPBitmap;
begin
LGPBitmap := TGPBitmap.Create(FExtendCtrl.Width, FExtendCtrl.Height);
FCanDrawFlag := False;
DrawRoutine(nil, LGPBitmap);
FCanDrawFlag := True;
Result := GetExtendGPbitmap(LGPBitmap);
end;
//-----------------------------------------------------------------------------
// リージョンの作成用関数
// 透明色以外のピクセルをリージョンにする
// このリージョンを利用して実際の描画開始位置とサイズを取得する
//-----------------------------------------------------------------------------
function TplGPResizeLine.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;
//-----------------------------------------------------------------------------
// 実際の描画処理を実行するDrawRoutineメソッド
// 引数のACanvasにこのメソッドの描画コードを実行する
//
// 描画開始位置は(DrawLeft, DrawTop)
// 幅と高さは DrawWidth, DrawHeight
//-----------------------------------------------------------------------------
procedure TplGPResizeLine.DrawRoutine(ACanvas: TCanvas; AGPBitmap: TGPBitmap;
AOffsetX: Integer; AOffsetY: Integer);
var
LGPGraphic : TGPGraphics;
LGPStartArrow : TGPAdjustableArrowCap;
LGPEndArrow : TGPAdjustableArrowCap;
LPenWidth : Single;
LhBitmap : HBITMAP;
LBitmap : TBitmap;
LRetGPBitmap : TGPBitmap;
LStartX : Integer;
LStartY : Integer;
LEndX : Integer;
LEndY : Integer;
begin
inherited;
//---------------------------------------------------------------------------
// 自分自身以外への描画とAGPBitmapへの描画処理(画像として描画する場合)
// GetGPBitmapメソッドで取得したTGPBitmapを引数のAGPBitmapに描画する
// GetGPBitmapはこのDrawRoutineを実行して取得している
// そこでフラグを使用して2度実行しないようにしている
//---------------------------------------------------------------------------
if ACanvas <> nil then begin
//描画コードはサイズを拡張したTGraphicControlに実行する
if ACanvas = Self.Canvas then ACanvas := FExtendCtrl.Canvas;
if (FCanDrawFlag) and (ACanvas <> FExtendCtrl.Canvas) then begin
LBitmap := TBitmap.Create;
LRetGPBitmap := GetGPBitmap;
try
LBitmap.AlphaFormat := afDefined;
LRetGPBitmap.GetHBITMAP(0, LhBitmap);
LBitmap.Handle := LhBitmap;
ACanvas.Draw(AOffsetX, AOffsetY, LBitmap);
finally
LBitmap.Free;
end;
exit;
end;
end else
if (FCanDrawFlag) and (AGPBitmap <> nil) then begin
LRetGPBitmap := GetGPBitmap;
LGPGraphic := TGPGraphics.Create(AGPBitmap);
try
LGPGraphic.DrawImage(LRetGPBitmap, AOffsetX, AOffsetY);
finally
FreeAndNil(LGPGraphic);
FreeAndNil(LRetGPBitmap);
end;
exit;
end;
//---------------------------------------------------------------------------
//実際の描画用コントロールの設定
FExtendCtrl.SetBounds(Left - FExtendHorz, Top - FExtendVert,
Width + FExtendHorz * 2, Height + FExtendVert * 2);
if Assigned(FGPPath) then FreeAndNil(FGPPath);
if Assigned(FGPPen) then FreeAndNil(FGPPen);
FGPPen := TGPPen.Create(FPenColor);
//拡大表示の設定
LPenWidth := FPenWidth * ZoomValue / 100;
if LPenWidth < 1.0 then LPenWidth := 1.0;
FGPPen.SetWidth(LPenWidth);
LGPStartArrow := TGPAdjustableArrowCap.Create(FArrowHeight, FArrowWidth, True);
LGPEndArrow := TGPAdjustableArrowCap.Create(FArrowHeight, FArrowWidth, True);
if FStartArrow then begin
LGPStartArrow.SetMiddleInset(FMiddleInset);
FGPPen.SetCustomStartCap(LGPStartArrow);
end;
if FEndArrow then begin
LGPEndArrow.SetMiddleInset(FMiddleInset);
FGPPen.SetCustomEndCap(LGPEndArrow);
end;
if ACanvas = nil then begin
LGPGraphic := TGPGraphics.Create(AGPBitmap);
end else begin
LGPGraphic := TGPGraphics.Create(ACanvas.Handle);
end;
FGPPath := TGPGraphicsPath.Create;
try
//-------------------------------------------------------------------------
// ここで描画の始点と終点を設定する
// AddLineメソッドの最初の引数が描画の始点,次が描画の終点
// X座標値にはFExtendHorzを,Y座標値にはFExtendVertを必ず加算しておく
//-------------------------------------------------------------------------
LStartX := DrawLeft;
LStartY := DrawTop;
LEndX := DrawRight;
LEndY := DrawBottom;
if FLineStartPos.X > FLineEndPos.X then begin
LStartX := DrawRight;
LEndX := DrawLeft;
end;
if FLineStartPos.Y > FLineEndPos.Y then begin
LStartY := DrawBottom;
LEndY := DrawTop;
end;
if FLineType = ltHorz then begin
LStartY := (LStartY + LEndY) div 2;
LEndY := LStartY;
end else
if FLineType = ltVert then begin
LStartX := (LStartX + LEndX) div 2;
LEndX := LStartX;
end;
FGPPath.AddLine(MakePoint(LStartX + FExtendHorz, LStartY + FExtendVert),
MakePoint(LEndX + FExtendHorz, LEndY + FExtendVert));
//-------------------------------------------------------------------------
if AntiAliasing and (ZoomValue = 100) then begin
LGPGraphic.SetSmoothingMode(SmoothingModeAntiAlias);
end;
LGPGraphic.DrawPath(FGPPen, FGPPath);
finally
FreeAndNil(LGPStartArrow);
FreeAndNil(LGPEndArrow);
FreeAndNil(LGPGraphic);
end;
end;
{ TplExtendResizeLine }
//=============================================================================
// コンストラクタ
//=============================================================================
constructor TplExtendResizeLine.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ParentColor := False;
Canvas.Brush.Style := bsClear;
FLineCtrl := TplGPResizeLine(AOwner);
end;
//=============================================================================
// CM_HITTESTメッセージ処理
// マウスイベントは全て透過とする
//=============================================================================
procedure TplExtendResizeLine.CMHitTest(var Message: TCMHitTest);
begin
Message.Result := HTNOWHERE;
end;
//=============================================================================
// ペイントメソッド
// ここで描画ルーチンを実行する
//=============================================================================
procedure TplExtendResizeLine.Paint;
begin
inherited;
FLineCtrl.DrawRoutine(Canvas, nil);
end;
end.
|