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







画像とオブジェクトの回転


GDI+ には,画像などのオブジェクトの回転や移動の操作として,以下のような関数類があります.
  • TGPImage.RotateFlip (画像の回転と反転.TGPImage, TGPBitmap 専用)
  • TGPGraphics.RotateTransform (軸の回転による描画)
  • TGPMatrix (ワールド変換行列を使用するアフィイン変換)
  • TGPGraphicsPath.Warp (歪曲変形.TGPGraphicsPath 専用)
このページのサンプルは,RotateFlip と RotateTransform 関数を使用したものです.前のページで説明したページ座標系を使用しています.
RotateTransform 関数による回転の手順は,次のようになります.
  • (1) TranslateTransform で座標の原点 (0, 0) を設定する.ここが回転の中心となる
  • (2) RotateTransform 関数で回転角度を設定する
  • (3) 画像や図形の描画コードを書く
通常の描画コードに,TranslateTransform と RotateTransform 関数を実行する 2 行のコードを追加するだけです.ただし,Translateform 関数で原点を移動している場合は,描画位置に注意します.
TranslaateTrnsform 関数で座標の原点を設定しない場合は,原点 (0, 0) は描画先の Canvas の左上のままです.したがって,ここが回転の中心にとなります.





01_RotateFlip 関数による画像の回転と反転


RotateFlip 関数を使用したサンプルです.RotateFlip 関数による回転と反転は,図形オブジェクトの回転とは違い,画像の中身そのものが回転あるいは反転します.

RotateFlip 関数は,次表の TRotateFlipType 列挙型の値を引数にして実行します.回転角度は時計回りが正の値となります.

表1 
TRotateFlipType の列挙型の値

RotateNoneFlipNone 回転も反転もなし
Rotate90FlipNone 90 度回転.反転なし
Rotate180FlipNone 180 度回転.反転なし
Rotate270FlipNone 270 度回転.反転なし
RotateNoneFlipX 回転はなし.水平方向に反転
Rotate90FlipX 90 度回転と水平方向の反転
Rotate180FlipX 180度回転と水平方向の反転
Rotate270FlipX 270 度回転と水平方向の反転
RotateNoneFlipY = Rotate180FlipX 回転はなし垂直方向の反転
Rotate90FlipY = Rotate270FlipX 90 度回転と垂直方向の反転
Rotate180FlipY = RotateNoneFlipX 180 度回転と垂直方向の反転
Rotate270FlipY = Rotate90FlipX 270 度回転と垂直方向の反転
RotateNoneFlipXY = Rotate180FlipNone 回転なし水平垂直方向の反転
Rotate90FlipXY = Rotate270FlipNone 90 度回転と水平と垂直方向の反転
Rotate180FlipXY = RotateNoneFlipNone 180 度回転と水平と垂直方向の反転
Rotate270FlipXY = Rotate90FlipNone 270 度回転と水平と垂直方向の反転


図1
設計時画面
図2
実行開始画面
図3
180 度回転
図4
90 度回転して横方向反転

リスト1
RotateFlip 関数による画像の回転と反転のテスト
unit Unit1;

interface

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

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

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

var
  Form1: TForm1;

implementation

uses 
  TypInfo;

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  LTypeInfo : PTypeInfo;
  LInfoData : PTypeData;
  i         : Integer;
begin
  Caption := 'GDI+  RotateFlip';

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


  //列挙型の最小値と最大値を取得
  LTypeInfo := TypeInfo(TRotateFlipType);
  LInfoData := GetTypeData(LTypeInfo);

  //列挙型の値を文字列として取得.RadioGroup1に表示
  for i := LInfoData^.MinValue to LInfoData^.MaxValue do begin
    RadioGroup1.Items.Add(GetEnumName(TypeInfo(TRotateFlipType), i));
  end;
  RadioGroup1.ItemIndex := 0;
end;

//=============================================================================
//  RadioGroup1のOnClickイベント処理
//  RotateFilpの値を変更したら,TMyPanelを再描画
//=============================================================================
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.RotateFlipNo := RadioGroup1.ItemIndex;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタ
//  各種プロパティと内部で使用する変数の初期化
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FRotateFlipNo := 0;
end;

//-----------------------------------------------------------------------------
//  RotateFlipNoプロパティの設定用メソッド
//  回転と反転の設定が変更になったら再描画
//-----------------------------------------------------------------------------
procedure TMyPanel.SetRotateFlipNo(const Value: Integer);
begin
  if not Assigned(Parent) then exit;

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

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

  //画像の表示サイズ
  LObjWidth  := 150;
  LObjHeight := 100;

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

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

  //回転と反転を設定
  LGPImage.RotateFlip(TRotateFlipType(FRotateFlipNo));
  //描画
  LGPGraphic.DrawImage(LGPImage, 20, 20, LObjWidth, LObjHeight);

  LGPImage.Free;
  LGPGraphic.Free;
end;

end.




02_RotateTransform 関数による画像オブジェクトの回転


これ以降は全て,RotateTransform 関数を使用したサンプルです.
サンプルコードは,前の項の RotateFlip 関数による実行結果との違いが確認できるようにしてみました.回転角度は,トラックバーで設定します.

下の図は,左から,元の画像,RotateFlip 関数による 90 度の回転,RotateTransform 関数で 90 度回転した結果です.共に時計回り (右周り) の回転結果です.





図5
設計時画面
図6
実行開始画面
図7
回転した状態
図8
回転の中心を変更
  • 画像の中心が回転の中心となる


前のページで,座標軸のスケーリングを説明しましたが,次のように,すると,座標軸のスケーリングもできます.このコードは,X 軸方向はそのままで,Y 軸方向を 1.5 倍にしています.
これを実行すると,下図のように図形が変形し,領域の形が平行四辺形になります.

  GDPGraphic.TranslateTransform(XLocation, YLocation);
  GDPGraphic.ScaleTransform(1.0, 1.5);


図9
スケーリングの例

リスト2
RotateTransform による画像オブジェクトの回転
unit Unit1;

interface

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

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

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

var
  Form1: TForm1;

implementation

uses 
  TypInfo;

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
  LTypeInfo : PTypeInfo;
  LInfoData : PTypeData;
  i         : Integer;
begin
  Caption := 'GDI+  画像オブジェクトの回転';

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

  //列挙型の最小値と最大値を取得
  LTypeInfo := TypeInfo(TRotateFlipType);
  LInfoData := GetTypeData(LTypeInfo);

  //列挙型の値を文字列として取得してRadioGroup1に表示
  for i := LInfoData^.MinValue  to LInfoData^.MaxValue do begin
    RadioGroup1.Items.Add(GetEnumName(TypeInfo(TRotateFlipType), i));
  end;
  RadioGroup1.ItemIndex := 0;

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

//=============================================================================
//  RadioGroup1のOnClickイベント処理
//  回転と反転のタイプの設定
//=============================================================================
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.RotateFlipNo := RadioGroup1.ItemIndex;
end;

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

//=============================================================================
//  CheckBox1のOnClickイベント
//  回転の中心座標の指定
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.IsDrawCenter := CheckBox1.Checked;
end;

{ TMyPanel }

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

  FRotateFlipNo := 0;
  FRotateAngle  := 0;
  FIsDrawCenter := 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;

//-----------------------------------------------------------------------------
//  IsDrawCenterプロパティ設定用メソッド
//  IsDrawCenterプロパティは,回転の中心座標の指定
//-----------------------------------------------------------------------------
procedure TMyPanel.SetIsDrawCenter(const Value: Boolean);
begin
  if not Assigned(Parent) then exit;

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

//-----------------------------------------------------------------------------
//  RotateFlipNoプロパティ設定用メソッド
//  画像そのものの回転と反転表示の設定
//-----------------------------------------------------------------------------
procedure TMyPanel.SetRotateFlipNo(const Value: Integer);
begin
  if not Assigned(Parent) then exit;

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

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

  //画像の表示サイズ
  LObjWidth  := 130;
  LObjHeight := 100;

  //ページ座標系の原点座標の設定
  LXLocation := Width  div 2;
  LYLocation := Height div 2;

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

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

  //座標の原点を(LXLocation,LYLocation)のページ座標空間にする
  //以後,ResetTransformを実行するまでは,この点が(0, 0)となる
  LGPGraphic.TranslateTransform(LXLocation, LYLocation);
  //この行を実行するとX軸とY軸の倍率を変えることができる
  //LGPGraphic.ScaleTransform(1.0, 1.5);

  //画像のオブジェクトを回転する
  LGPGraphic.RotateTransform(FRotateAngle);

  //画像そのものの回転と反転もテストしてみる
  LGPImage.RotateFlip(TRotateFlipType(FRotateFlipNo));

  //---------------------------------------------------------------------------
  //  画像の描画
  //  TranslateTransformで設定した位置が描画原点(0, 0)となっている
  //  その原点から相対的な描画開始位置(左上の座標)を設定する
  //  つまり,ページ座標空間を基準とした描画コードとなる
  //  FIsDrawCenterがTrueの時は,描画始点を表示サイズの半分だけ左と上にずらす
  //---------------------------------------------------------------------------

  if FIsDrawCenter then begin
    LGPGraphic.DrawImage(
      LGPImage, -LObjWidth div 2, -LObjHeight div 2, LObjWidth, LObjHeight);
  end else begin
    LGPGraphic.DrawImage(LGPImage, 0, 0, LObjWidth, LObjHeight);
  end;

  //---------------------------------------------------------------------------
  //  描画の基準座標をワールド座標空間に戻す
  //  描画の原点がこのコントロールの左上となる
  //  ページ座標空間の時の(0, 0)を交点とした十字線を描く
  //  ページ座標空間のままで描画してもよいが,ScaleTransformを実行していると,
  //  線の太さなども,設定した比率で拡大または縮小されてしまう
  //---------------------------------------------------------------------------

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

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

end.




03_回転した画像オブジェクトの描画領域の矩形


回転した画像を TImage 等に表示する場合,TImage のサイズを,回転後の画像を完全に含むサイズにしないと,描画が欠けてしまうことになります.
そのためには,回転後の画像を完全に含む矩形領域を取得する必要があります.前のページで説明で使用した座標値の変換関数では,座標の回転を含む場合,取得でないようです.

そこで,このサンプルでは,TGPMatrix とパスの性質を利用しています.TGPMatrix による画像の変換は座標軸の回転はしません.全てワールド座標値で行われます.矩形のパス図形を生成し,画像と同じ操作を TGPMatrix で設定します.その結果を,以下のリンクのサンプルで説明した GetBounds 関数で取得します.


図10
設計時画面
図11
画像描画領域の矩形
  • 回転した画像は矩形内にある

リスト3
回転した画像オブジェクトの描画領域を取得
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     : Integer;
  LYLocation     : Integer;
  LImageFilePath : String;
  LGPImage       : TGPImage;
  LGPGraphic     : TGPGraphics;
  LGPMatrix      : TGPMatrix;
  LGPPath        : TGPGraphicsPath;
  LSizeRect      : TGPRect;
  LGPPen         : TGPPen;
begin
  inherited;

  //画像の表示サイズ
  LObjWidth  := 130;
  LObjHeight := 100;

  //ページ座標系の原点座標の設定
  LXLocation := Width  div 2;
  LYLocation := Height div 2;

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

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

  //座標の原点を(LXLocation,LYLocation)のページ座標空間にする
  //以後,ResetTransformを実行するまでは,この点が(0, 0)となる
  LGPGraphic.TranslateTransform(LXLocation, LYLocation);
  //この行を実行するとX軸とY軸の倍率を変えることができる
  //LGPGraphic.ScaleTransform(1.0, 1.5);

  //画像のオブジェクトを回転する
  LGPGraphic.RotateTransform(FRotateAngle);

  //---------------------------------------------------------------------------
  //  画像の描画
  //  TranslateTransformで設定した位置が描画原点(0, 0)となっている
  //  その原点から相対的な描画開始位置(左上の座標)を設定する
  //  つまり,ページ座標空間を基準とした描画コードとなる
  //  回転の中心座標を(LXLocation, LYLocation)とするために,左と上に,オブジェク
  //  トの幅と高さの半分だけ,描画の始点をずらす
  //---------------------------------------------------------------------------

  LGPGraphic.DrawImage(
    LGPImage, -LObjWidth div 2, -LObjHeight div 2, LObjWidth, LObjHeight);


  //---------------------------------------------------------------------------
  //  以下,画像オブジェクトの描画の矩形領域取得用のコード
  //  取得した領域の矩形を表示する
  //
  //  描画の基準座標をワールド座標空間に戻し,
  //  描画の原点をこのコントロールの左上にする
  //  パスを生成し,四角形を追加.TGPMarixの機能を使用して,上と同じページ座標
  //  空間にして,同じ操作実行後に,パスのGetBounds関数で領域を取得
  //---------------------------------------------------------------------------

  //ワールド座標空間に戻す
  LGPGraphic.ResetTransform;

  //パスを生成して四角形を追加
  LGPPath := TGPGraphicsPath.Create;
  LGPPath.AddRectangle(MakeRect(
    -LObjWidth div 2, -LObjHeight div 2, LObjWidth, LObjHeight));

  //TGPMatrixを生成して座標基準を平行移動して回転操作の情報を格納
  LGPMatrix := TGPMatrix.Create;
  LGPMatrix.Translate(LXLocation, LYLocation);
  //TGPGraphics.ScaleTransformを実行した場合は,このコードを実行する
  //LGPMatrix.Scale(1.0, 1.5);
  LGPMatrix.Rotate(FRotateAngle);

  //そのTGPMatrixの情報でパス情報を更新
  LGPPath.Transform(LGPMatrix);

  //パスが含まれる矩形領域を取得
  LGPPath.GetBounds(LSizeRect);
  //TGPPenを生成して,そのペンで,取得した矩形領域の矩形を描画
  LGPPen := TGPPen.Create(aclBlack);
  LGPGraphic.DrawRectangle(LGPPen, LSizeRect);

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

end.




04_TImage のサイズを設定して回転画像を表示


前の項でテストした結果を,実際に TImage に適用したサンプルです.
このサンプルでは,描画用の TMyPanel は必要ないので,生成していません.


図12
設計時画面
図13
実行開始画面
図14
回転した例
  • 回転する度に TImage のサイズを計算して設定している

次のコードのように,SetColorKey で透過色を設定すれば,ビットマップを透過表示できます.

リスト4
ビットマップの透過設定
var
  LGPImageAttr : TGPImageAttributes;
  LColor       : Cardinal;
begin
  //
  //
  //画像の(0, 0)の点を透過色とする
  LGPImage.GetPixel(0,0, LColor);
  LGPImageAttr := TGPImageAttributes.Create;
  LGPImageAttr.SetColorKey(LColor, LColor);

  //LGPImageを描画する
  LGPGraphic.DrawImage(
              LGPImage,
              MakeRect(-LObjWidth div 2, -LObjHeight div 2, LObjWidth, LObjHeight),
              0,
              0,
              LGPImage.GetWidth,
              LGPImage.GetHeight,
              UnitPixel,
              LGPImageAttr);
  //
  //

図15
ビットマップを透過表示する

また,Image1.Transparent := True; とすれば,TImage も透過します.


図16
Transparent をTrue にした場合

リスト5
TImage コントロールに回転画像を表示
unit Unit1;

interface

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

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

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption := 'GDI+  RotateTransform TImage';
  Self.DoubleBuffered := True;

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

  TrackBar1Change(nil);
end;

//=============================================================================
//  TrackBar1のOnChangeイベント
//  TrackBar1の値を変更したら,TImageに再描画
//=============================================================================
procedure TForm1.TrackBar1Change(Sender: TObject);
var
  LObjWidth      : Integer;
  LObjHeight     : Integer;
  LRotateAngle   : Integer;
  LStartX        : Integer;
  LStartY        : Integer;
  LImageFilePath : String;
  LGPImage       : TGPBitmap;
  LMatrix        : TGPMatrix;
  LGPPath        : TGPGraphicsPath;
  LSizeRect      : TGPRect;
  LGPGraphic     : TGPGraphics;
begin
  //回転角はTrackBar1の値
  LRotateAngle := TrackBar1.Position;

  //画像ファイルを指定.TGPBitmapを生成して格納
  LImageFilePath := ExtractFilePath(ParamStr(0)) + '503.bmp';
  LGPImage       := TGPBitmap.Create(LImageFilePath);

  //TGPBitmap.GetWidth,TGPBitmap.GetHeightでイメージの幅と高さが取得できるが,
  //ここでは,別の幅と高さで描画する(TCanvasのStretchDraw相当の描画)
  LObjWidth  := 200;
  LObjHeight := 150;

  //描画の基準点(回転の中心座標)を表示サイズの中央とする
  LStartX := LObjWidth  div 2;
  LStartY := LObjHeight div 2;

  //---------------------------------------------------------------------------
  //  以下,TImageのサイズを計算するための処理
  //  画像オブジェクトの描画の矩形領域取得用のコード
  //  パスを生成し,四角形を追加.TGPMarixの機能を使用して,描画するオブジェク
  //  に対する基準座標を平行移動して,回転の操作を行う
  //---------------------------------------------------------------------------

  //このCanvasを描画先にしてTGPGraphicsオブジェクトを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //パスを生成し,四角形を追加
  LGPPath := TGPGraphicsPath.Create;
  LGPPath.AddRectangle(
    MakeRect(-LObjWidth div 2, -LObjHeight div 2, LObjWidth, LObjHeight));

  //TGPMatrixを生成して座標基準を平行移動して回転操作の情報を格納
  LMatrix := TGPMatrix.Create;
  LMatrix.Translate(LStartX, LStartY);
  LMatrix.Rotate(LRotateAngle);

  //そのTGPMatrixの情報でパス情報を更新
  LGPPath.Transform(LMatrix);
  //パスの描画領域を示す矩形情報を取得
  LGPPath.GetBounds(LSizeRect);

  LMatrix.Free;
  LGPPath.Free;
  LGPGraphic.Free;
  //---------------------------------------------------------------------------
  //  描画の矩形情報の取得終了
  //  LSizeRect(TGPRect)に描画領域の矩形情報が格納されている
  //---------------------------------------------------------------------------


  //---------------------------------------------------------------------------
  //  以下,TImageへの描画コード
  //---------------------------------------------------------------------------

  //描画の位置とサイズの設定
  LStartX := LSizeRect.Width  div 2;
  LStartY := LSizeRect.Height div 2;

  //TImageの描画を消去し,取得したサイズに設定.背景を塗りつぶす
  Image1.Picture.Assign(nil);
  Image1.SetBounds(Image1.Left, Image1.Top, LSizeRect.Width, LSizeRect.Height);
  Image1.Canvas.Brush.Color := clSilver;
  Image1.Canvas.FillRect(Image1.Canvas.ClipRect);

  //描画先をTImage.CanvasとしてTGPGraphicsオブジェクトを生成
  LGPGraphic := TGPGraphics.Create(Form1.Image1.Canvas.Handle);
  //ワールド座標空間に戻す
  LGPGraphic.ResetTransform;

  //座標原点を移動し回転する
  LGPGraphic.TranslateTransform(LStartX, LStartY);
  LGPGraphic.RotateTransform(LRotateAngle);

  //LGPImageを描画する
  LGPGraphic.DrawImage(
    LGPImage, -LObjWidth div 2, -LObjHeight div 2, LObjWidth, LObjHeight);

  LGPGraphic.Free;
  LGPImage.Free;
end;

end.




05_RotateTransform 関数によるブラシの回転


ブラシも回転できますが,ソリッドブラシだけは回転できません.
たとえ回転できたとしても,何にも変化がないでしょう.

グラデーションブラシの回転で,座標原点の移動,スケーリング等を,経時的に変化させれば,幻想的な世界が創れるかも知れません.
残念ですが,このサンプルはテクスチャブラシの例です.しかも,毎度おなじみの「ヒヨコ」の画像となっています.しかも回転だけです.サイズの変化もしません.
トラックバー,または矢印キーを操作すると回転します.楽しんでください.


図17
設計時画面
図18
実行開始画面
図19
回転した状態

リスト6
テクスチャでブラシの回転
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
  LImageFilePath : String;
  LGPImage       : TGPImage;
  LGPGraphic     : TGPGraphics;
  LGPRect        : TGPRect;
  LGPBrush       : TGPTextureBrush;
  LGPPen         : TGPPen;
begin
  inherited;

  //画像ファイルを指定.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);

  //リセット
  LGPBrush.ResetTransform;
  //回転の中心を設定
  LGPBrush.TranslateTransform(Width / 2, Height / 2);
  //軸の倍率を変更して,サイズを小さくする
  LGPBrush.ScaleTransform(0.5, 0.5);
  //回転角度の指定
  LGPBrush.RotateTransform(FRotateAngle);
  //楕円を描画,内部を指定したブラシで塗潰す
  LGPGraphic.FillEllipse(LGPBrush, LGPRect);
  //外周を描く
  LGPPen := TGPPen.Create(aclGray);
  LGPGraphic.DrawEllipse(LGPPen, LGPRect);

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

end.




06_RotateTransform 関数による文字列の回転


文字列の回転のサンプルです.


図20
設計時画面
図21
実行開始画面
図22
回転した状態

リスト7
文字列の回転のサンプル
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, 45, Self.ClientWidth - 10, Self.ClientHeight - 50);
    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  : Integer;
  LYLocation  : Integer;
  LFontSize   : Single;
  LGPGraphic  : TGPGraphics;
  LGPFont     : TGPFont;
  LGPBrush    : TGPSolidBrush;
  LGPPen      : TGPPen;
begin
  inherited;

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

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

  //座標の原点を(LXLocation,LYLocation)のページ座標空間にする
  //以後,ResetTransformを実行するまでは,この点が(0, 0)となる
  LGPGraphic.TranslateTransform(LXLocation, LYLocation);

  //回転角度の指定
  LGPGraphic.RotateTransform(FRotateAngle);

  //TGPFontと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(0.0, -LFontSize /2.0), LGPBrush);

  //ワールド座標(Canvasの左上が(0, 0)の座標系)に変換して十字線を描画
  LGPGraphic.ResetTransform;
  LGPPen := TGPPen.Create(aclBlack);
  LGPPen.SetDashStyle(DashStyleDash);
  LGPGraphic.DrawLine(LGPPen, 0, LYLocation, Width, LYLocation);
  LGPGraphic.DrawLine(LGPPen, LXLocation, 0, LXLocation, Height);


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

end.




07_RotateTransform 関数による図形の回転


図形の回転のサンプルです.
RotateTransform 関数で座標軸を回転すると,この関数実行後の描画コードは,全てこのページ空間の設定で実行されます.確認のため,文字列も描画してみました.また,X 軸と Y 軸を独立してスケーリングできるようにしています.


図23
設計時画面
図24
実行開始画面
図25
回転した状態

下図は X 軸に対してだけスケーリングをした結果です.X 軸と Y 軸のスケールが異なっているために変形して,平行四辺形になっています.


図26
スケーリング

リスト8
図形の回転とスケーリング
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;
    procedure FormCreate(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
    procedure TrackBar2Change(Sender: TObject);
    procedure TrackBar3Change(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, 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;

{ 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メソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LXLocation  : Integer;
  LYLocation  : Integer;
  LFontSize   : Single;
  LGPGraphic  : TGPGraphics;
  LGPFont     : TGPFont;
  LGPBrush    : TGPSolidBrush;
  LGPPen      : TGPPen;
begin
  inherited;

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

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

  //座標の原点を(LXLocation,LYLocation)のページ座標空間にする
  //以後,ResetTransformを実行するまでは,この点が(0, 0)となる
  LGPGraphic.TranslateTransform(LXLocation, LYLocation);

  //X軸とY軸のスケーリング
  LGPGraphic.ScaleTransform(FXScale, FYScale);

  //回転角度の指定
  LGPGraphic.RotateTransform(FRotateAngle);

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

  //TGPFontとTGPSolidBrushのオブジェクトを生成し設定
  //文字のサイズをピクセル(ドット)単位で指定して文字列を描画
  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.0, 55), LGPBrush);


  LGPPen.Free;
  //ワールド座標(Canvasの左上が(0, 0)の座標系)に変換して十字線を描画
  LGPGraphic.ResetTransform;
  LGPPen := TGPPen.Create(aclBlack);
  LGPPen.SetDashStyle(DashStyleDash);
  LGPGraphic.DrawLine(LGPPen, 0, LYLocation, Width, LYLocation);
  LGPGraphic.DrawLine(LGPPen, LXLocation, 0, LXLocation, Height);


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

end.




08_RotateTransform 関数によるパスの回転


パス専用の回転用の関数はありません.TGPGraphics のオブジェクトとして操作します.次のページのサンプルで使用している TGPMatrix を利用すると,パスだけを回転させることができます.

このサンプルでは,パスのオブジェクトを生成し,楕円を追加しています.そして,そのパスをクリップしています.クリップすると,それ以降の描画内容は,このパスでクリップされます.
このことを利用して,このサンプルでは画像を表示してみました.パスを回転すると,表示している画像も回転します.

バスは,直線や曲線を組み合わせて,より複雑な図形を描くことができます.また,パスの座標をマウスで操作するようにもできます.柔軟性があります.
画像の一部を,そのようなパスの形で切り取ったり,貼り付けたりすれば,いろいろな画像処理のプログラムができそうです.
リージョン (TGPRegion) を使用しても同様なことができます.


図27
設計時画面
図28
実行開始画面
  • パスなので,いろいろな形に切り抜きができる
図29
回転した状態

リスト9
パスの回転
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
  LImageFilePath : String;
  LGPImage      : TGPImage;
  LObjWidth      : Integer;
  LObjHeight     : Integer;
  LXLocation     : Integer;
  LYLocation     : Integer;
  LGPPath        : TGPGraphicsPath;
  LGPGraphic     : TGPGraphics;
  LGPPen         : TGPPen;
begin
  inherited;

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

  //オブジェクトの表示サイズ
  LObjWidth  := 250;
  LObjHeight := 188;

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

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

  //座標の原点を(LXLocation,LYLocation)にする
  //以後,ResetTransformを実行するまでは,この点が(0, 0)となる
  LGPGraphic.TranslateTransform(LXLocation, LYLocation);

  //回転角度の指定
  LGPGraphic.RotateTransform(FRotateAngle);

  //パスを生成して楕円を追加
  LGPPath := TGPGraphicsPath.Create;
  LGPPath.AddEllipse(-LObjWidth div 2, -LObjHeight div 2, LObjWidth, LObjHeight);
  //このパスで,以降の描画をクリップ
  LGPGraphic.SetClip(LGPPath);
  //画像を描画
  LGPGraphic.DrawImage(LGPImage, MakeRect(-LObjWidth div 2,
                                          -LObjHeight div 2,
                                          LObjWidth,
                                          LObjHeight));

  //クリップを解除
  LGPGraphic.ResetClip;
  //左上が(0, 0)の座標系に変換して十字線を描画
  LGPGraphic.ResetTransform;
  //描画に使用するペンを生成
  LGPPen := TGPPen.Create(aclGray);
  LGPPen.SetDashStyle(DashStyleDash);
  LGPGraphic.DrawLine(LGPPen, 0, LYLocation, Width, LYLocation);
  LGPGraphic.DrawLine(LGPPen, LXLocation, 0, LXLocation, Height);


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

end.