Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2012年12月10日] [更新 2017年05月13日] GDI+ 関係サンプル
GDI+ 関係サンプル 
G090_TGPMatrix による画像とオブジェクトの回転
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
G090_GDIPlus_TGPMatrixRotateAt.zip [4,686 KB] 2017年05月13日版 (EXE 同梱)







TGPMatrix によるオブジェクトの回転


TGPMatrix は,座標変換に使用する 3 行 3 列のマトリックス (行列) です.
この行列の各要素に値を与えて,座標の線形変換と平行移動を行いますが,回転だけは特別扱いになっています.オブジェクトの回転用の関数が別に用意されています.これらは下表のようなものです.

RotateAt 関数を使用して回転する場合は,実際の原点移動はしません.座標系はワールド座標系のままです.内部で原点移動と回転と同じ処理をした描画を自動でしてくれます.手順としては,
  • (1) TGPMatrix.Create で TGPMatrix のオブジェクトを生成する
  • (2) TGPMatrix.RotateAt で回転の角度と回転の中心座標を設定する
  • (3) Transform または SetTransform 関数で TGPMatrix の設定内容をセットする
  • (4) 通常通り,画像や図形の描画コードを書く
となります.
このページのサンプルは,これらの関数類を使用した画像などの回転のプログラムです.

表1
GPMatrix の原点移動などの関数類

回転角度と回転中心座標の同時設定 TGPMatrix.RotateAt
描画座標は,ワールド座標系を使用する
回転角度と回転中心座標の個別設定 TGPMatrix.Translate
TGPMatrix.Rotate
描画座標は,Translaate で設定した座標を原点とした座標で指定する
スケーリングする場合はこの組合せを使用する
スケーリング TGPMatrix.Scale





01_TGPMatrix によるパス図形の回転


以下のリンクのサンプルで,パス図形の回転のコードを使っています.確認と若干の動作説明用に掲載しておきます.

このサンプルでは,TGPMatrix の RotateAt 関数で,回転角度と回転中心の座標を設定した後,バスの Transform 関数でそれらの情報をセットしています.パスは,この情報に基づいて,設定した座標を中心とした回転図形を描画することになります.


図1
設計時画面
図2
実行開始画面

次の図は,回転したところです.赤い線は,ワールド座標の原点 (0, 0) から回転中心の座標まで引いた直線です.TGPMatrix.RotateAt 関数で回転しても,座標系に変化がないことが確認できます.

点線の矩形は,このパス図形を囲む領域ですが,実際には,線の太さの半分だけ少ないサイズとなっています.太い直線は,指定した座標に対して,両側にその太さ分を振り分けて描画します.そのためにこのようになります.


図3
回転した結果
  • 点線は,図形を含む領域の矩形

リスト1
TGPMatrix によるパス図形の回転
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, GDIPAPI, GDIPOBJ, ComCtrls;

type
  TMyPanel = class(TPanel)
  private
    FRotateAngle  : Integer;
    procedure SetRotateAngle(const Value: Integer);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    property RotateAngle  : Integer read FRotateAngle  write SetRotateAngle;
  end;

  TForm1 = class(TForm)
    TrackBar1: TTrackBar;
    procedure FormCreate(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption := 'GDI+  パスの回転と描画領域の取得';

  //描画するコントロールを生成
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent := Self;
    SetBounds(5, 40, Self.ClientWidth - 10, Self.ClientHeight - 45);
    DoubleBuffered   := True;
    ControlStyle     := MyPanel.ControlStyle + [csOpaque];
    BevelOuter       := bvNone;
    Visible          := True;
    Color            := clWhite;
    ParentBackground := False;
    Anchors          := [akLeft, akTop, akRight, akBottom];
  end;

  //回転角度変更用のTrackBar1の設定
  //回転角度は-360~360度とする
  with TrackBar1 do begin
    ShowSelRange := False;
    TickStyle    := tsNone;
    Height   := 26;
    Min      := -360;
    Max      := 360;
    Position := 0;
  end;
end;

//=============================================================================
//  TrackBar1のOnClickイベント処理
//  オブジェクトの回転角度の設定の変更
//=============================================================================
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.RotateAngle := TrackBar1.Position;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタ
//  各種プロパティを初期値を設定
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FRotateAngle  := 0;
end;

//-----------------------------------------------------------------------------
//  RotateAngleプロパティ設定用メソッド
//  オブジェクトの回転角度の設定
//-----------------------------------------------------------------------------
procedure TMyPanel.SetRotateAngle(const Value: Integer);
begin
  if not Assigned(Parent) then exit;

  if FRotateAngle <> Value then begin
    FRotateAngle := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LObjWidth  : Integer;
  LObjHeight : Integer;
  LXLocation : Single;
  LYLocation : Single;
  LGPGraphic : TGPGraphics;
  LGPPath    : TGPGraphicsPath;
  LGPMatrix  : TGPMatrix;
  LGPPen     : TGPPen;
  LSizeRect  : TGPRectF;
begin
  inherited;

  //描画する四角形のサイズ
  LObjWidth  := 150;
  LObjHeight := 100;

  //回転の中心とする座標値を設定
  LXLocation := Width  div 2;
  LYLocation := Height div 2;

  //このTMyPanelのCanvasを描画先にしてTGPGraphicsのインスタンスを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //パスを生成して四角形を追加
  //描画始点を,回転の中心座標から幅と高さの半分だけ移動
  LGPPath := TGPGraphicsPath.Create;
  LGPPath.AddRectangle(MakeRect(
    LXLocation - LObjWidth / 2, LYLocation - LObjHeight / 2, LObjWidth, LObjHeight));

  //TGPMatrixを生成.回転角度と回転の中心座標を指定する
  LGPMatrix := TGPMatrix.Create;
  LGPMatrix.RotateAt(FRotateAngle, MakePoint(LXLocation, LYLocation));

  //そのTGPMatrixの情報を追加
  LGPPath.Transform(LGPMatrix);

  //TGPPenを生成して,そのペンでバス図形を描画
  //一応アンチエイリアシング処理を行う
  LGPPen := TGPPen.Create(aclGreen, 10);
  LGPGraphic.SetSmoothingMode(SmoothingModeAntiAlias);
  LGPGraphic.DrawPath(LGPPen, LGPPath);

  //---------------------------------------------------------------------------
  //  実際のパス図形の描画はここまで
  //---------------------------------------------------------------------------


  //ワールド座標系の原点から回転中心座標まで赤い線を引く
  //ページ座標系になっていないことを確認
  LGPPen.SetColor(aclRed);
  LGPPen.SetWidth(1.0);
  LGPGraphic.DrawLine(LGPPen, 0.0, 0.0, LXLocation, LYLocation);

  //パスの座標が含まれる矩形領域を取得
  LGPPath.GetBounds(LSizeRect);
  //取得した領域の矩形を描画
  LGPPen.SetColor(aclGray);
  LGPPen.SetDashStyle(DashStyleDash);
  LGPGraphic.DrawRectangle(LGPPen, LSizeRect);

  //Createしたオブジェクトは解放処理する
  LGPMatrix.Free;
  LGPPen.Free;
  LGPPath.Free;
  LGPGraphic.Free;
end;

end.


[G080] [08_RotateTransform 関数によるパスの回転] のように,画像表示を伴う場合は,TGPMatrix の設定内容を,TGPGraphic.SetTransform でセットします.
これは,次の画像の回転のサンプルと同じです.


図4
画像表示を伴う場合

TGPMatrix によるパスの回転.画像表示を伴う場合
このコードの実行結果は,上の画像
procedure TMyPanel.Paint;
var
  LImageFilePath : String;
  LGPImage       : TGPImage;
  LObjWidth      : Integer;
  LObjHeight     : Integer;
  LXLocation     : Single;
  LYLocation     : Single;
  LGPGraphic     : TGPGraphics;
  LGPPath        : TGPGraphicsPath;
  LGPMatrix      : TGPMatrix;
  LGPPen         : TGPPen;
begin
  inherited;

  //画像ファイルを指定.TGPImaageインスタンスを生成して読み込む
  LImageFilePath := ExtractFilePath(ParamStr(0)) + 'Hachi.bmp';
  LGPImage      := TGPImage.Create(LImageFilePath);

  //描画サイズ
  LObjWidth  := 220;
  LObjHeight := 150;

  //回転の中心とする座標値
  LXLocation := Width  div 2;
  LYLocation := Height div 2;

  //このTMyPanelのCanvasを描画先にしてTGPGraphicsのインスタンスを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //パスを生成して楕円を追加
  LGPPath := TGPGraphicsPath.Create;
  LGPPath.AddEllipse(
    LXLocation - LObjWidth / 2, LYLocation - LObjHeight / 2, LObjWidth, LObjHeight);

  //TGPMatrixを生成.回転角度と回転の中心座標を指定する
  LGPMatrix := TGPMatrix.Create;
  LGPMatrix.RotateAt(FRotateAngle, MakePoint(LXLocation, LYLocation));

  //そのTGPMatrixの情報を追加
  LGPGraphic.SetTransform(LGPMatrix);

  //パスで,以降の描画をクリップ
  //この設定は,回転角度と中心座標の設定後(RotateAt)に実行しないと無効
  LGPGraphic.SetClip(LGPPath);

  //画像を描画
  LGPGraphic.DrawImage(LGPImage, MakeRect(
    LXLocation - LObjWidth / 2, LYLocation - LObjHeight / 2, LObjWidth, LObjHeight));

  //クリップを解除して,パスの輪郭を描く
  LGPGraphic.ResetClip;
  LGPPen := TGPPen.Create(aclBlack);
  LGPGraphic.DrawPath(LGPPen, LGPPath);


  //Createしたオブジェクトは解放処理する
  LGPImage.Free;
  LGPMatrix.Free;
  LGPPath.Free;
  LGPGraphic.Free;
  LGPPen.Free;
end;




02_TGPMatrix による画像の回転 - RotateAt


図形の回転のサンプルです.
画像,パスではない図形,文字列の場合は,TGPMatrix の RotateAt 関数で,回転角度と回転中心の座標を設定した後,下のコードのように,TGPGraphics の SetTransform 関数でセットします.


  TGPPath.Transform(TGPMatrix);         //パスの場合
  TGPRegion.Transform(TGPMatrix);       //リージョンの場合
  TGPBrush.SetTransform(TGPMatrix);     //ブラシの場合
  TGPGraphic.SetTransform(TGPMatrix);   //画像・図形・文字列の場合


図5
設計時画面
図6
実行開始画面
図7
回転した状態

リスト2
TGPMatrix による画像の回転
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, GDIPAPI, GDIPOBJ, ComCtrls;

type
  TMyPanel = class(TPanel)
  private
    FRotateAngle  : Integer;
    procedure SetRotateAngle(const Value: Integer);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    property RotateAngle  : Integer read FRotateAngle  write SetRotateAngle;
  end;

  TForm1 = class(TForm)
    TrackBar1: TTrackBar;
    procedure FormCreate(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption := 'GDI+  画像の回転';

  //描画するコントロールを生成
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent := Self;
    SetBounds(5, 40, Self.ClientWidth - 10, Self.ClientHeight - 45);
    DoubleBuffered   := True;
    ControlStyle     := MyPanel.ControlStyle + [csOpaque];
    BevelOuter       := bvNone;
    Visible          := True;
    Color            := clWhite;
    ParentBackground := False;
    Anchors          := [akLeft, akTop, akRight, akBottom];
  end;

  //回転角度変更用のTrackBar1の設定
  //回転角度は-360~360度とする
  with TrackBar1 do begin
    ShowSelRange := False;
    TickStyle    := tsNone;
    Height   := 26;
    Min      := -360;
    Max      := 360;
    Position := 0;
  end;
end;

//=============================================================================
//  TrackBar1のOnClickイベント処理
//  オブジェクトの回転角度の設定の変更
//=============================================================================
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.RotateAngle := TrackBar1.Position;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタ
//  各種プロパティを初期値を設定
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FRotateAngle  := 0;
end;

//-----------------------------------------------------------------------------
//  RotateAngleプロパティ設定用メソッド
//  オブジェクトの回転角度の設定
//-----------------------------------------------------------------------------
procedure TMyPanel.SetRotateAngle(const Value: Integer);
begin
  if not Assigned(Parent) then exit;

  if FRotateAngle <> Value then begin
    FRotateAngle := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LObjWidth      : Integer;
  LObjHeight     : Integer;
  LXLocation     : Single;
  LYLocation     : Single;
  LImageFilePath : String;
  LGPImage       : TGPImage;
  LGPGraphic     : TGPGraphics;
  LGPMatrix      : TGPMatrix;
begin
  inherited;

  //描画する四角形のサイズ
  LObjWidth  := 150;
  LObjHeight := 100;

  //回転の中心とする座標値を設定
  LXLocation := Width  div 2;
  LYLocation := Height div 2;

  //このTMyPanelのCanvasを描画先にしてTGPGraphicsのインスタンスを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //画像ファイルを指定.TGPImaageインスタンスを生成して読み込む
  LImageFilePath := ExtractFilePath(ParamStr(0)) + '503.bmp';
  LGPImage       := TGPImage.Create(LImageFilePath);

  //TGPMatrixを生成.回転角度と回転の中心座標を指定する
  LGPMatrix := TGPMatrix.Create;
  LGPMatrix.RotateAt(FRotateAngle, MakePoint(LXLocation, LYLocation));

  //そのTGPMatrixの情報をTGPGraphicsに追加
  //SetTransformは軸の回転でオブジェクトを回転する
  LGPGraphic.SetTransform(LGPMatrix);

  //LGPImageを描画
  //描画始点は,回転中心座標から画像の幅と高さの半分だけ移動した位置
  LGPGraphic.DrawImage(LGPImage,
                       LXLocation - LObjWidth  div 2,
                       LYLocation - LObjHeight div 2,
                       LObjWidth,
                       LObjHeight);

  //Createしたオブジェクトは解放処理する
  LGPImage.Free;
  LGPGraphic.Free;
  LGPMatrix.Free;
end;

end.




03_TGPMatrix による画像の回転 - Translate と Rotate


同じ画像の回転ですが,こちらは,Translate 関数で回転中心の座標を指定し,Rotate で回転角度を指定する場合です.この場合は,オブジェクトの描画座標を,回転中心の座標を原点 (0, 0) とした座標系で指定します.
動作内容は,上のサンプルと全く同じです.

リスト3
TGPMatrix による画像の回転 - Translate と Rotate を使用した場合
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, GDIPAPI, GDIPOBJ, ComCtrls;

type
  TMyPanel = class(TPanel)
  private
    FRotateAngle  : Integer;
    procedure SetRotateAngle(const Value: Integer);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    property RotateAngle  : Integer read FRotateAngle  write SetRotateAngle;
  end;

  TForm1 = class(TForm)
    TrackBar1: TTrackBar;
    procedure FormCreate(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption := 'GDI+  画像の回転';

  //描画するコントロールを生成
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent := Self;
    SetBounds(5, 40, Self.ClientWidth - 10, Self.ClientHeight - 45);
    DoubleBuffered   := True;
    ControlStyle     := MyPanel.ControlStyle + [csOpaque];
    BevelOuter       := bvNone;
    Visible          := True;
    Color            := clWhite;
    ParentBackground := False;
    Anchors          := [akLeft, akTop, akRight, akBottom];
  end;

  //回転角度変更用のTrackBar1の設定
  //回転角度は-360~360度とする
  with TrackBar1 do begin
    ShowSelRange := False;
    TickStyle    := tsNone;
    Height   := 26;
    Min      := -360;
    Max      := 360;
    Position := 0;
  end;
end;

//=============================================================================
//  TrackBar1のOnClickイベント処理
//  オブジェクトの回転角度の設定の変更
//=============================================================================
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.RotateAngle := TrackBar1.Position;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタ
//  各種プロパティを初期値を設定
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FRotateAngle  := 0;
end;

//-----------------------------------------------------------------------------
//  RotateAngleプロパティ設定用メソッド
//  オブジェクトの回転角度の設定
//-----------------------------------------------------------------------------
procedure TMyPanel.SetRotateAngle(const Value: Integer);
begin
  if not Assigned(Parent) then exit;

  if FRotateAngle <> Value then begin
    FRotateAngle := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//  TranslateとRotateを使用した回転
//  TranslateとRotateを使用した場合,描画コードの座標はページ座標系を使用する
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LObjWidth      : Integer;
  LObjHeight     : Integer;
  LXLocation     : Single;
  LYLocation     : Single;
  LImageFilePath : String;
  LGPImage       : TGPImage;
  LGPGraphic     : TGPGraphics;
  LGPMatrix      : TGPMatrix;
begin
  inherited;

  //描画する四角形のサイズ
  LObjWidth  := 150;
  LObjHeight := 100;

  //回転の中心とする座標値を設定
  //このサンプルでは,この座標が原点となる
  LXLocation := Width  div 2;
  LYLocation := Height div 2;

  //このTMyPanelのCanvasを描画先にしてTGPGraphicsのインスタンスを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //画像ファイルを指定.TGPImaageインスタンスを生成して読み込む
  LImageFilePath := ExtractFilePath(ParamStr(0)) + '503.bmp';
  LGPImage       := TGPImage.Create(LImageFilePath);

  //TGPMatrixを生成.回転の中心座標と回転の角度を設定
  LGPMatrix := TGPMatrix.Create;
  LGPMatrix.Translate(LXLocation, LYLocation);
  LGPMatrix.Rotate(FRotateAngle);

  //そのTGPMatrixの情報をTGPGraphicsに追加
  //SetTransformは軸の回転でオブジェクトを回転する
  LGPGraphic.SetTransform(LGPMatrix);

  //描画始点は,回転中心座標から画像の幅と高さの半分だけ移動した位置
  LGPGraphic.DrawImage(
    LGPImage, -LObjWidth div 2, -LObjHeight div 2, LObjWidth, LObjHeight);

  //Createしたオブジェクトは解放処理する
  LGPImage.Free;
  LGPGraphic.Free;
  LGPMatrix.Free;
end;

end.




04_TGPMatrix によるリニアグラデーションブラシの回転


リニアグラデーションブラシの回転例です.
ブラシの場合は,TGPMatrix の RotateAt 関数で,回転角度と回転中心の座標を設定した後,該当するブラシオブジェクトの SetTransform 関数でセットします.


図8
設計時画面
図9
実行開始時の画面
図10
回転した状態

ブラシの SetTransform 関数ではなく, TGPGraphics の SetTransform 関数で TGPMatrix で設定した内容をセットすると,図形の回転となります.サンプルにチェックボックスを配置して,これを確認できるようにしています.


図11
図形を回転した場合

リスト4
TGPMatrix によるリニアグラデーションブラシの回転
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, GDIPAPI, GDIPOBJ, ComCtrls;

type
  TMyPanel = class(TPanel)
  private
    FRotateAngle  : Integer;
    procedure SetRotateAngle(const Value: Integer);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    property RotateAngle  : Integer read FRotateAngle  write SetRotateAngle;
  end;

  TForm1 = class(TForm)
    TrackBar1: TTrackBar;
    CheckBox1: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption := 'GDI+  リニアグラデーションブラシの回転';

  //描画するコントロールを生成
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent := Self;
    SetBounds(5, 60, Self.ClientWidth - 10, Self.ClientHeight - 65);
    DoubleBuffered   := True;
    ControlStyle     := MyPanel.ControlStyle + [csOpaque];
    BevelOuter       := bvNone;
    Visible          := True;
    Color            := clWhite;
    ParentBackground := False;
    Anchors          := [akLeft, akTop, akRight, akBottom];
  end;

  //回転角度変更用のTrackBar1の設定
  //回転角度は-360~360度とする
  with TrackBar1 do begin
    ShowSelRange := False;
    TickStyle    := tsNone;
    Height   := 26;
    Min      := -360;
    Max      := 360;
    Position := 0;
  end;
end;

//=============================================================================
//  TrackBar1のOnClickイベント処理
//  オブジェクトの回転角度の設定
//=============================================================================
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.RotateAngle := TrackBar1.Position;
end;

//=============================================================================
//  CheckBox1のOnClickイベント処理
//  ブラシではなく,図形を回転させる
//  これは,TMyPanelのプロパティにはしていない.直接,値を変更しているので注意
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.Invalidate;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタ
//  各種プロパティを初期値を設定
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FRotateAngle  := 0;
end;

//-----------------------------------------------------------------------------
//  RotateAngleプロパティ設定用メソッド
//  オブジェクトの回転角度の設定
//-----------------------------------------------------------------------------
procedure TMyPanel.SetRotateAngle(const Value: Integer);
begin
  if not Assigned(Parent) then exit;

  if FRotateAngle <> Value then begin
    FRotateAngle := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LXLocation  : Single;
  LYLocation  : Single;
  LGPGraphic  : TGPGraphics;
  LGPRect     : TGPRect;
  LGPBrush    : TGPLinearGradientBrush;
  LGPMatrix   : TGPMatrix;
  LGPPen      : TGPPen;
begin
  inherited;

  //回転の中心とする座標値を設定
  LXLocation := Width  div 2;
  LYLocation := Height div 2;

  //このTMyPanelのCanvasを描画先にしてTGPGraphicsのインスタンスを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //描画領域を設定
  LGPRect := MakeRect(0, 0, Width - 1, Height -1);

  //リニアグラデーションのブラシを生成
  LGPBrush := TGPLinearGradientBrush.Create(
                          LGPRect,
                          MakeColor(255, 0, 255, 0),
                          MakeColor(255, 0, 20,  255),
                          LinearGradientModeVertical);

  //TGPMatrixを生成.回転角度と回転の中心座標を指定する
  LGPMatrix := TGPMatrix.Create;
  LGPMatrix.RotateAt(FRotateAngle, MakePoint(LXLocation, LYLocation));

  //ブラシではなく,図形を回転するとどんな感じなるか見てみる
  if Form1.CheckBox1.Checked then begin
    LGPGraphic.SetTransform(LGPMatrix);
  end else begin
    LGPBrush.SetTransform(LGPMatrix);
  end;

  //楕円を描画,内部をブラシで塗潰す
  LGPGraphic.SetSmoothingMode(SmoothingModeAntiAlias);
  LGPGraphic.FillEllipse(LGPBrush, LGPRect);
  //外周を描く
  LGPPen := TGPPen.Create(aclGray);
  LGPGraphic.DrawEllipse(LGPPen, LGPRect);

  //Createしたオブジェクトは解放処理する
  LGPGraphic.Free;
  LGPBrush.Free;
  LGPMatrix.Free;
  LGPPen.Free;
end;

end.




05_TGPMatrix によるテクスチャブラシの回転


[G080] [05_RotateTransform 関数によるブラシの回転] でテクスチャブラシのサンプルを掲載しましたので,比較用のサンプルです.


図12
設計時画面
図13
実行開始時の画面
図14
回転した状態

リスト5
TGPMatrix によるテクスチャブラシの回転
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, GDIPAPI, GDIPOBJ, ExtDlgs, ComCtrls;

type
  TMyPanel = class(TPanel)
  private
    FRotateAngle  : Integer;
    procedure SetRotateAngle(const Value: Integer);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    property RotateAngle  : Integer read FRotateAngle  write SetRotateAngle;
  end;

  TForm1 = class(TForm)
    TrackBar1: TTrackBar;
    procedure FormCreate(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption := 'GDI+  テクスチャブラシの回転';

  //描画するコントロールを生成
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent := Self;
    SetBounds(5, 40, Self.ClientWidth - 10, Self.ClientHeight - 45);
    DoubleBuffered   := True;
    ControlStyle     := MyPanel.ControlStyle + [csOpaque];
    BevelOuter       := bvNone;
    Visible          := True;
    Color            := clWhite;
    ParentBackground := False;
    Anchors          := [akLeft, akTop, akRight, akBottom];
  end;

  //回転角度変更用のTrackBar1の設定
  //回転角度は-360~360度とする
  with TrackBar1 do begin
    ShowSelRange := False;
    TickStyle    := tsNone;
    Height   := 26;
    Min      := -360;
    Max      := 360;
    Position := 0;
  end;
end;

//=============================================================================
//  TrackBar1のOnClickイベント処理
//  オブジェクトの回転角度の設定
//=============================================================================
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.RotateAngle := TrackBar1.Position;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタ
//  各種プロパティを初期値を設定
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FRotateAngle  := 0;
end;

//-----------------------------------------------------------------------------
//  RotateAngleプロパティ設定用メソッド
//  オブジェクトの回転角度の設定
//-----------------------------------------------------------------------------
procedure TMyPanel.SetRotateAngle(const Value: Integer);
begin
  if not Assigned(Parent) then exit;

  if FRotateAngle <> Value then begin
    FRotateAngle := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LXLocation     : Single;
  LYLocation     : Single;
  LImageFilePath : String;
  LGPImage       : TGPImage;
  LGPGraphic     : TGPGraphics;
  LGPRect        : TGPRect;
  LGPBrush       : TGPTextureBrush;
  LGPMatrix      : TGPMatrix;
  LGPPen         : TGPPen;
begin
  inherited;

  //回転の中心とする座標値を設定
  LXLocation := Width  div 2;
  LYLocation := Height div 2;

  //画像ファイルを指定.TGPImaageインスタンスを生成して読み込む
  LImageFilePath := ExtractFilePath(ParamStr(0)) + '502.gif';
  LGPImage       := TGPImage.Create(LImageFilePath);

  //このTMyPanelのCanvasを描画先にしてTGPGraphicsのインスタンスを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //描画領域の設定
  LGPRect := MakeRect(0, 0, Width - 1, Height - 1);

  //画像をTGPTextuerBrushに指定する
  LGPBrush := TGPTextureBrush.Create(LGPImage);

  //TGPMatrixを生成.回転角度と回転の中心座標を指定する
  LGPMatrix := TGPMatrix.Create;
  LGPMatrix.RotateAt(FRotateAngle, MakePoint(LXLocation, LYLocation));
  LGPMatrix.Scale(0.5, 0.5);
  //そのTGPMatrixの情報をTGPBrushに追加
  LGPBrush.SetTransform(LGPMatrix);

  //楕円を描画,内部をブラシで塗潰す
  LGPGraphic.FillEllipse(LGPBrush, LGPRect);
  //外周を描く
  LGPPen := TGPPen.Create(aclGray);
  LGPGraphic.DrawEllipse(LGPPen, LGPRect);

  //Createしたオブジェクトは解放処理する
  LGPImage.Free;
  LGPGraphic.Free;
  LGPBrush.Free;
  LGPMatrix.Free;
  LGPPen.Free;
end;

end.




06_TGPMatrix による文字列の回転


文字列の回転も同じです.文字列の場合は,TGPMatrix の RotateAt 関数で,回転角度と回転中心の座標を設定した後,TGPGraphics の SetTransform 関数で TGPMatrix の情報をセットします.
TGPMatrix によるオブジェクトの回転は,座標軸を回転しています.これを確認するために,Canvas に十字線を描画するコードを追加しています.
チェックボックスをチェックすると,座標軸を回転しない時の十字線となります.


図15
設計時画面
図16
実行開始時の画面
図17
回転した状態
  • 軸の回転を確認できる

下の図は,ResetTransform 関数を実行してから十字線を描画した場合です.この関数は,変換行列の状態をリセットして,単位行列にします.
この関数は,ワールド座標系に戻す時にも使用します.


図18
軸の回転がない状態
  • 変換行列をリセットしてから十字線を描画している

リスト6
TGPMatrix による文字列の回転
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, GDIPAPI, GDIPOBJ, ComCtrls;

type
  TMyPanel = class(TPanel)
  private
    FRotateAngle  : Integer;
    procedure SetRotateAngle(const Value: Integer);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    property RotateAngle  : Integer read FRotateAngle  write SetRotateAngle;
  end;

  TForm1 = class(TForm)
    TrackBar1: TTrackBar;
    CheckBox1: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption := 'GDI+  文字列の回転';

  //描画するコントロールを生成
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent := Self;
    SetBounds(5, 60, Self.ClientWidth - 10, Self.ClientHeight - 65);
    DoubleBuffered   := True;
    ControlStyle     := MyPanel.ControlStyle + [csOpaque];
    BevelOuter       := bvNone;
    Visible          := True;
    Color            := clWhite;
    ParentBackground := False;
    Anchors          := [akLeft, akTop, akRight, akBottom];
  end;

  //回転角度変更用のTrackBar1の設定
  //回転角度は-360~360度とする
  with TrackBar1 do begin
    ShowSelRange := False;
    TickStyle    := tsNone;
    Height   := 26;
    Min      := -360;
    Max      := 360;
    Position := 0;
  end;
end;

//=============================================================================
//  TrackBar1のOnClickイベント処理
//  オブジェクトの回転角度の設定
//=============================================================================
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.RotateAngle := TrackBar1.Position;
end;

//=============================================================================
//  CheckBox1のOnClickイベント処理
//  軸の回転をリセットして描画する指定
//  これは,TMyPanelのプロパティにはしていない.直接,値を変更しているので注意
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.Invalidate;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタ
//  各種プロパティを初期値を設定
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FRotateAngle := 0;
end;

//-----------------------------------------------------------------------------
//  RotateAngleプロパティ設定用メソッド
//  オブジェクトの回転角度の設定
//-----------------------------------------------------------------------------
procedure TMyPanel.SetRotateAngle(const Value: Integer);
begin
  if not Assigned(Parent) then exit;

  if FRotateAngle <> Value then begin
    FRotateAngle := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LXLocation : Single;
  LYLocation : Single;
  LFontSize  : Single;
  LGPGraphic : TGPGraphics;
  LGPFont    : TGPFont;
  LGPBrush   : TGPSolidBrush;
  LGPPen     : TGPPen;
  LGPMatrix  : TGPMatrix;
begin
  inherited;

  //回転の中心とする座標値を設定
  LXLocation := Width  div 2;
  LYLocation := Height div 2;

  //このTMyPanelのCanvasを描画先にしてTGPGraphicsのインスタンスを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //TGPMatrixを生成.回転角度と回転の中心座標を指定する
  LGPMatrix := TGPMatrix.Create;
  LGPMatrix.RotateAt(FRotateAngle, MakePoint(LXLocation, LYLocation));

  //そのTGPMatrixの情報をTGPGraphicsに追加
  //SetTransformは軸の回転でオブジェクトを回転する
  LGPGraphic.SetTransform(LGPMatrix);

  //TGFontとTGPSolidBrushのオブジェクトを生成し設定
  //文字のサイズをピクセル単位で指定
  LFontSize := 20.0;
  LGPFont  := TGPFont.Create('Arial', LFontSize, FontStyleRegular, UnitPixel);
  LGPBrush := TGPSolidBrush.Create(MakeColor(255, 0, 0, 0));

  //Canvasに文字列を描画
  //回転するとジャッギーが目立つのでアンチエイリアス処理を実行
  LGPGraphic.SetTextRenderingHint(TextRenderingHintAntiAlias);
  LGPGraphic.DrawString('文字列の回転',
                        - 1,
                        LGPFont,
                        MakePoint(LXLocation, LYLocation - LFontSize /2.0),
                        LGPBrush);


  //軸の回転をリセットして十字線を描画
  if Form1.CheckBox1.Checked then begin
    LGPGraphic.ResetTransform;
  end;

  LGPPen := TGPPen.Create(aclBlack);
  LGPPen.SetDashStyle(DashStyleDash);
  LGPGraphic.DrawLine(LGPPen, 0, LYLocation, Width, LYLocation);
  LGPGraphic.DrawLine(LGPPen, LXLocation, 0, LXLocation, Height);

  //Createしたオブジェクトは解放処理する
  LGPGraphic.Free;
  LGPFont.Free;
  LGPBrush.Free;
  LGPPen.Free;
  LGPMatrix.Free;
end;

end.




07_TGPMatrix による図形の回転


[G080] [07_RotateTransform 関数による図形の回転] のサンプルと同じ様に,X 軸と Y 軸を独立してスケーリングできるようにしています.
このサンプルでは,まず TGPMatrix.Translate で回転中心の座標を設定し,Scale 関数でスケーリングの設定をしてから Rotate で回転の角度を設定しています.
回転中心座標と回転角度を別々に独立して設定した場合は,描画コードの座標として,回転中心座標を原点 (0, 0) とした座標で指定します.


図19
設計時画面
図20
実行開始時の画面
図21
回転した状態
図22
X 軸をスケーリング

上の図は,X 軸のスケーリングをしてから回転した場合です.これを回転してからスケーリングすると下図のように図形が変形し,平行四辺形となります.


図23
回転後にスケーリング

リスト7
TGPMatrix による図形の回転
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, GDIPAPI, GDIPOBJ, ComCtrls;

type
  TMyPanel = class(TPanel)
  private
    FRotateAngle  : Integer;
    FXScale       : Single;
    FYScale       : Single;
    procedure SetRotateAngle(const Value: Integer);
    procedure SetXScale(const Value: Single);
    procedure SetYScale(const Value: Single);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    property RotateAngle  : Integer read FRotateAngle  write SetRotateAngle;
    property XScale       : Single  read FXScale       write SetXScale;
    property YScale       : Single  read FYScale       write SetYScale;
  end;

  TForm1 = class(TForm)
    TrackBar1: TTrackBar;
    TrackBar2: TTrackBar;
    TrackBar3: TTrackBar;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    CheckBox1: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
    procedure TrackBar2Change(Sender: TObject);
    procedure TrackBar3Change(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption := 'GDI+  TGPMatrix による図形の回転';

  //描画するコントロールを生成
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent := Self;
    SetBounds(5, 110, Self.ClientWidth - 10, Self.ClientHeight - 115);
    DoubleBuffered   := True;
    ControlStyle     := MyPanel.ControlStyle + [csOpaque];
    BevelOuter       := bvNone;
    Visible          := True;
    Color            := clWhite;
    ParentBackground := False;
    Anchors          := [akLeft, akTop, akRight, akBottom];
  end;

  //回転角度変更用のTrackBar1の設定
  //回転角度は-360~360度とする
  with TrackBar1 do begin
    ShowSelRange := False;
    TickStyle    := tsNone;
    Height   := 26;
    Min      := -360;
    Max      := 360;
    Position := 0;
  end;

  //X軸のスケーリングの設定
  with TrackBar2 do begin
    ShowSelRange := False;
    TickStyle    := tsNone;
    Height   := 26;
    Min      := 10;
    Max      := 30;
    Position := 10;
  end;

  //Y軸のスケーリングの設定
  with TrackBar3 do begin
    ShowSelRange := False;
    TickStyle    := tsNone;
    Height   := 26;
    Min      := 10;
    Max      := 30;
    Position := 10;
  end;
end;

//=============================================================================
//  TrackBar1のOnClickイベント処理
//  オブジェクトの回転角度の設定
//=============================================================================
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.RotateAngle := TrackBar1.Position;
end;

//=============================================================================
//  TrackBar2のOnClickイベント処理
//  X軸のスケーリングの設定
//=============================================================================
procedure TForm1.TrackBar2Change(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.XScale := TrackBar2.Position / 10;
end;

//=============================================================================
//  TrackBar3のOnClickイベント処理
//  Y軸のスケーリングの設定
//=============================================================================
procedure TForm1.TrackBar3Change(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.YScale := TrackBar3.Position / 10;
end;

//=============================================================================
//  CheckBox1のOnClickイベント処理
//  スケーリングしてから回転するか,回転してからスケーリングするかの指定
//  これは,TMyPanelのプロパティにはしていない.直接,値を変更しているので注意
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.Invalidate;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタ
//  各種プロパティを初期値を設定
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FRotateAngle := 0;
  FXScale := 1.0;
  FYScale := 1.0;
end;

//-----------------------------------------------------------------------------
//  RotateAngleプロパティ設定用メソッド
//  オブジェクトの回転角度の設定
//-----------------------------------------------------------------------------
procedure TMyPanel.SetRotateAngle(const Value: Integer);
begin
  if not Assigned(Parent) then exit;

  if FRotateAngle <> Value then begin
    FRotateAngle := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  XScaleプロパティ設定用メソッド
//  X軸のスケーリングの設定
//-----------------------------------------------------------------------------
procedure TMyPanel.SetXScale(const Value: Single);
begin
  if not Assigned(Parent) then exit;

  if FXScale <> Value then begin
    FXScale := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  YScaleプロパティ設定用メソッド
//  Y軸のスケーリングの設定
//-----------------------------------------------------------------------------
procedure TMyPanel.SetYScale(const Value: Single);
begin
  if not Assigned(Parent) then exit;

  if FYScale <> Value then begin
    FYScale := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//  TranslateとRotateを使用した回転
//  TranslateとRotateを使用した場合,描画コードの座標はページ座標系を使用する
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LObjWidth  : Integer;
  LObjHeight : Integer;
  LXLocation : Single;
  LYLocation : Single;
  LFontSize  : Single;
  LGPGraphic : TGPGraphics;
  LGPFont    : TGPFont;
  LGPBrush   : TGPSolidBrush;
  LGPPen     : TGPPen;
  LGPMatrix  : TGPMatrix;
begin
  inherited;

  //描画する四角形のサイズ
  LObjWidth  := 80;
  LObjHeight := 50;

  //座標原点の移動位置の設定
  LXLocation := Width  / 2;
  LYLocation := Height / 2;

  //このTMyPanelのCanvasを描画先にしてTGPGraphicsのインスタンスを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //TGPMatrixを生成.回転角度と回転の中心座標を指定する
  LGPMatrix := TGPMatrix.Create;
  LGPMatrix.Translate(LXLocation, LYLocation);

  if Form1.CheckBox1.Checked then begin
    //スケーリングしてから回転する
    LGPMatrix.Scale(FXScale, FYScale);
    LGPMatrix.Rotate(FRotateAngle);
  end else begin
    //回転してからスケーリングする
    LGPMatrix.Rotate(FRotateAngle);
    LGPMatrix.Scale(FXScale, FYScale);
  end;

  //そのTGPMatrixの情報をTGPGraphicsに追加
  LGPGraphic.SetTransform(LGPMatrix);

  //四角形を描画
  LGPPen := TGPPen.Create(aclGreen, 7);
  LGPGraphic.SetSmoothingMode(SmoothingModeAntiAlias);
  LGPGraphic.DrawRectangle(LGPPen, 0, 0, LObjWidth, LObjHeight);

  //文字のサイズをピクセル単位で指定して文字列を描画
  LFontSize := 20.0;
  LGPFont  := TGPFont.Create('Arial', LFontSize, FontStyleRegular, UnitPixel);
  LGPBrush := TGPSolidBrush.Create(MakeColor(255, 0, 0, 0));
  //回転するとジャッギーが目立つのでアンチエイリアス処理を実行
  LGPGraphic.SetTextRenderingHint(TextRenderingHintAntiAlias);
  LGPGraphic.DrawString('図形の回転',
                        - 1,
                        LGPFont,
                        MakePoint(0, LObjHeight + 5.0),
                        LGPBrush);


  //変換をリセットし,ワールド座標系で十字線を描画
  LGPGraphic.ResetTransform;

  LGPPen.SetColor(aclBlack);
  LGPPen.SetWidth(1);
  LGPPen.SetDashStyle(DashStyleDash);
  LGPGraphic.DrawLine(LGPPen, 0, LYLocation, Width, LYLocation);
  LGPGraphic.DrawLine(LGPPen, LXLocation, 0, LXLocation, Height);


  //Createしたオブジェクトは解放処理する
  LGPGraphic.Free;
  LGPFont.Free;
  LGPBrush.Free;
  LGPPen.Free;
  LGPMatrix.Free;
end;

end.




08_リージョンの結合と TGPMatrix によるリージョンの回転


リージョンの回転のサンプルプログラムです.
単に回転だけでは寂しいので,リージョンの結合のテストも兼ねてみました.RadioGroup1 の Item によって,下表にようにリージョンが変化するようになっています.「自分自身」であるリージョンは楕円のパスから生成します.楕円の下に矩形を描画し,楕円と,この矩形を結合して新しいリージョンの領域とします.ただし,Item が 1 の時だけは,矩形をリージョンとします.

表1
本サンプルプログラムでは,自分自身は楕円で,結合対象は矩形

0 楕円 楕円がリージョン
1 矩形 矩形がリージョン
2 Union 自分自身と結合対象となる領域全てがリージョンとなる.論理和
3 Intersect 自分自身と結合対象となる領域が重なる部分だけがリージョンとなる.論理籍
4 Exclude 自分だけの領域がリージョンとなる.結合対象の領域と重なる部分は含まれない
5 Xor_ 自分自身と結合対象が重なる部分以外がリージョンとなる
6 Complement 自分自身を含まない,結合対象の領域がリージョンとなる



図24
設計時画面
図25
実行開始時の画面
  • 楕円がリージョン
図26
回転した状態
  • リージョンだけが回転する
  • リージョンは「領域」なので,パスの輪郭は含まれない
図27
論理積結合の場合
  • やはりリージョンだけが回転する
図28
論理和結合の場合

チェックボックスをチェックすると,TGPGraphics のオブジェクト全体が回転の対象となります.
結果は下図のようになります.
楕円内に画像を描画するサンプルでも,パスの楕円ではなく,オブジェクト全体を操作対象とすることにより,楕円内の画像も回転させています.


図29
オブジェクト全体が回転
  • この時,座標軸の描画もオブジェクトなので回転してしまう (直線という図形)
  • そこで,ResetTransform を実行してから描画している

TGPMatrix の設定内容を,次のようにパスの情報として格納すると,パスだけの回転となります.
このサンプルプログラムでは,パスは楕円だけです.したがって,楕円だけが回転します.

  //そのTGPMatrixの情報を追加
//  if FIsAllObject then begin
//    //描画オブジェクト全体を対象にする場合
//    GDPGraphic.SetTransform(GDPMatrix);
//  end else begin
//    //現在のリージョンだけを対象にする場合
//    GDPRgn.Transform(GDPMatrix);
//  end;
  GDPPath.Transform(GDPMatrix);


図30
パスだけを回転

リスト8
リージョンの結合と TGPMatrix によるリージョンの回転
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, GDIPAPI, GDIPOBJ, ComCtrls;

type
  TMyPanel = class(TPanel)
  private
    FRotateAngle   : Integer;
    FRegionCombine : Integer;
    FIsAllObject   : Boolean;
    procedure SetRotateAngle(const Value: Integer);
    procedure SetIsAllObject(const Value: Boolean);
    procedure SetRegionCombine(const Value: Integer);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    property RotateAngle   : Integer read FRotateAngle   write SetRotateAngle;
    property RegionCombine : Integer read FRegionCombine write SetRegionCombine;
    property IsAllObject   : Boolean read FIsAllObject   write SetIsAllObject;
  end;

  TForm1 = class(TForm)
    TrackBar1: TTrackBar;
    RadioGroup1: TRadioGroup;
    CheckBox1: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
    procedure RadioGroup1Click(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption := 'GDI+  リージョンの結合とリージョンの回転';

  //描画するコントロールを生成
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent := Self;
    SetBounds(5, 180, Self.ClientWidth - 10, Self.ClientHeight - 185);
    DoubleBuffered   := True;
    ControlStyle     := MyPanel.ControlStyle + [csOpaque];
    BevelOuter       := bvNone;
    Visible          := True;
    Color            := clWhite;
    ParentBackground := False;
    Anchors          := [akLeft, akTop, akRight, akBottom];
  end;

  //回転角度変更用のTrackBar1の設定
  //回転角度は-360~360度とする
  with TrackBar1 do begin
    ShowSelRange := False;
    TickStyle    := tsNone;
    Height   := 26;
    Min      := -360;
    Max      := 360;
    Position := 0;
  end;

  //リージョンの結合方式
  RadioGroup1.Items.Add('楕円');
  RadioGroup1.Items.Add('矩形');
  RadioGroup1.Items.Add('Union');
  RadioGroup1.Items.Add('Intersect');
  RadioGroup1.Items.Add('Exclude');
  RadioGroup1.Items.Add('Xor_');
  RadioGroup1.Items.Add('Complement');

  RadioGroup1.ItemIndex := 0;
end;

//=============================================================================
//  TrackBar1のOnClickイベント処理
//  オブジェクトの回転角度の設定の変更
//=============================================================================
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.RotateAngle := TrackBar1.Position;
end;

//=============================================================================
//  RadioGroup1のOnClickイベント処理
//  リージョンの結合方式の選択
//=============================================================================
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
   TrackBar1.Position    := 0;
   MyPanel.RegionCombine := RadioGroup1.ItemIndex;

end;

//=============================================================================
//  CheckBox1のOnClickイベント処理
//  リージョンだけの回転かオブジェクト全てを回転させるかの選択
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
   MyPanel.IsAllObject := CheckBox1.Checked;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタ
//  各種プロパティを初期値を設定
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FRotateAngle   := 0;
  FRegionCombine := 0;
  FIsAllObject   := False;
end;

//-----------------------------------------------------------------------------
//  RotateAngleプロパティ設定用メソッド
//  オブジェクトの回転角度の設定
//-----------------------------------------------------------------------------
procedure TMyPanel.SetRotateAngle(const Value: Integer);
begin
  if not Assigned(Parent) then exit;

  if FRotateAngle <> Value then begin
    FRotateAngle := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  RegionCombineプロパティ設定用メソッド
//  リージョンの結合方式
//-----------------------------------------------------------------------------
procedure TMyPanel.SetRegionCombine(const Value: Integer);
begin
  if not Assigned(Parent) then exit;

  if FRegionCombine <> Value then begin
    FRegionCombine := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  IsAllObjectプロパティ設定用メソッド
//  リージョンだけの回転かオブジェクト全てを回転させるかの選択
//-----------------------------------------------------------------------------
procedure TMyPanel.SetIsAllObject(const Value: Boolean);
begin
  if not Assigned(Parent) then exit;

  if FIsAllObject <> Value then begin
    FIsAllObject := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LObjWidth      : Integer;
  LObjHeight     : Integer;
  LXLocation     : Single;
  LYLocation     : Single;
  LGPGraphic     : TGPGraphics;
  LGPPen         : TGPPen;
  LGPPath        : TGPGraphicsPath;
  LGPBrush       : TGPBrush;
  LGPRgn         : TGPRegion;
  LEllipseRect   : TGPRect;
  LRectangleRect : TGPRect;
  LGPMatrix      : TGPMatrix;
begin
  //楕円の描画サイズ
  LObjWidth  := 100;
  LObjHeight := 100;

  //回転の中心の座標値を設定
  LXLocation := Width  div 2;
  LYLocation := Height div 2;

  //描画先をこのCanvasとし,ペンとブラシを作成しておく
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);
  LGPPen     := TGPPen.Create(aclBlack);
  LGPBrush   := TGPSolidBrush.Create(aclSkyBlue);

  //パスを生成して楕円をバスに追加
  LGPPath := TGPGraphicsPath.Create;
  LGPPath.AddEllipse(LXLocation, LYLocation - LObjHeight div 2, LObjWidth, LObjHeight);

  //そのパスをリージョンに変換
  LGPRgn := TGPRegion.Create(LGPPath);

  //楕円の描画領域の矩形を取得
  //LGPRgn.GetBounds(LEllipseRect, LGPGraphic);//で取得しても同じ
  LGPPath.GetBounds(LEllipseRect);
  //下の矩形の描画始点を設定
  LRectangleRect   := LEllipseRect;
  LRectangleRect.Y := LEllipseRect.Y + LEllipseRect.Height div 2;

  //リージョンの結合の種類の設定.ここで設定したものが新しいリージョンとなる
  //0 の時は楕円がリージョン
  case FRegionCombine of
    1: LGPRgn := TGPRegion.Create(LRectangleRect);
    2: LGPRgn.Union(LRectangleRect);
    3: LGPRgn.Intersect(LRectangleRect);
    4: LGPRgn.Exclude(LRectangleRect);
    5: LGPRgn.Xor_(LRectangleRect);
    6: LGPRgn.Complement(LRectangleRect);
  end;

  //TGPMatrixを生成.回転角度と回転の中心座標を指定する
  LGPMatrix := TGPMatrix.Create;
  LGPMatrix.RotateAt(FRotateAngle, MakePoint(LXLocation, LYLocation));

  //そのTGPMatrixの情報を追加
  if FIsAllObject then begin
    //描画オブジェクト全体を対象にする場合
    LGPGraphic.SetTransform(LGPMatrix);
  end else begin
    //現在のリージョンだけを対象にする場合
    LGPRgn.Transform(LGPMatrix);
  end;

  //現在のリージョンを塗りつぶす
  LGPGraphic.FillRegion(LGPBrush, LGPRgn);
  LGPGraphic.SetSmoothingMode(SmoothingModeAntiAlias);
  LGPPen.SetWidth(3);
  //バス(楕円)の輪郭線を描き,矩形を描画
  LGPGraphic.DrawPath(LGPPen, LGPPath);
  LGPGraphic.DrawRectangle(LGPPen, LRectangleRect);

  //回転中心(LXLocation, LYLocation)を交点とする座標軸を描画
  //操作の対象がTGPGraphicsの場合はResetTransformにしないと軸も回転する
  LGPGraphic.ResetTransform;
  LGPPen.SetDashStyle(DashStyleDot);
  LGPPen.SetWidth(1);
  LGPGraphic.DrawLine(LGPPen, 0, LYLocation, Width, LYLocation);
  LGPGraphic.DrawLine(LGPPen, LXLocation, 0, LXLocation, Height);

  //Createしたオブジェクトは解放する
  LGPGraphic.Free;
  LGPPen.Free;
  LGPPath.Free;
  LGPBrush.Free;
  LGPRgn.Free;
  LGPMatrix.Free;
end;

end.