Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2017年05月02日] [更新 2017年05月24日] GDI+ 関係サンプル
GDI+ 関係サンプル 
G014_四角形・角丸四角形の描画
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
G014_GDIPlus_Rectangle.zip [1751 KB] 2017年05月24日版 (EXE 同梱)







GDI+ による四角形・角丸四角形の描画


GDI+ を使用して四角形,つまり矩形を描画するサンプルです.他のサンプルでも四角形を描画するコードがありますが,単独のサンプルとして掲載しておきます.
四角形内部の塗り潰しにはブラシを使用します.各種のブラシが GDI+ で利用できます.


[備考]
多角形は TGPGraphic.DrawPolygon で描画できます.
パスの場合は TGPGraphicsPath.AddPolygon で作成できます.座標値の配列を使用する場合は以下の記事を参考にしてください.静的配列の使用例と動的配列の使用法の説明があります.





01_GDI+ による四角形の描画


実際に四角形を描画するサンプルです.以下の手順で実行しています.
  • 四角形の輪郭線の描画に使用する色と線の太さを指定して TGPPen を生成する
  • 四角形の内部の塗り潰しの色を指定して TGPSolidBrush を生成する
  • 四角形のサイズを MakeRect で設定する
  • 四角形の内部を TGPGraphics.FillRectangle で塗り潰す
  • 四角形の輪郭線を TGPGraphics.DrawRectangle で描画する


図1
設計時画面
  • 描画用コントロールは実行時生成
図2
実行結果
  • [Button1] をクリックすると塗り潰しの色が変更できる

リスト1
GDI+ による四角形の描画
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  protected
    procedure Paint; override;
  public
    FBrushColor : Cardinal;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    ColorDialog1: TColorDialog;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

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

  //TMyPanelを生成して配置
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent  := Self;
    Left    := 5;
    Top     := 35;
    Width   := Self.ClientWidth  - 10;
    Height  := Self.ClientHeight - Top - 5;
    Anchors := [akLeft, akTop, akRight, akBottom];
    Color   := clWhite;
    ParentBackground := False;
    DoubleBuffered   := True;

    FBrushColor := aclAntiqueWhite;
  end;
end;

//=============================================================================
//  ブラシの色を変更する
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  ColorDialog1.Color := ARGBToColorRef(MyPanel.FBrushColor);
  if ColorDialog1.Execute then begin
    MyPanel.FBrushColor := ColorRefToARGB(ColorDialog1.Color);
  end;
  MyPanel.Invalidate;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//  四角形の例
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LGPGraphic : TGPGraphics;
 LGPPen     : TGPPen;
  LGPBrush   : TGPSolidBrush;
  LGPRect    : TGPRect;
  LLeft      : Integer;
  LTop       : Integer;
  LWidth     : Integer;
  LHeight    : Integer;
  LPenWidth  : Integer;
begin
  inherited;

  LLeft     := 20;
  LTop      := 20;
  LWidth    := 230;
  LHeight   := 130;
  LPenWidth := 3;


  //ペンのオブジェクトを生成
  LGPPen := TGPPen.Create(aclGreen, LPenWidth);
  //TGPSolidBrushを生成
  LGPBrush := TGPSolidBrush.Create(FBrushColor);

  //四角形のサイズを指定
  LGPRect := MakeRect(LLeft, LTop, LWidth, LHeight);

  //TGPGraphicsオブジェクトを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //内部を塗り潰す
  //塗り潰す必要がなければ実行する必要はない(ブラシの設定も不要)
  LGPGraphic.FillRectangle(LGPBrush, LGPRect);

  //四角形を描画
  //線の描画関係の関数名はDrawXXXX
  LGPGraphic.DrawRectangle(LGPPen, LGPRect);


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

end.




02_GDI+ による角が丸い四角形の描画


VCL には角の丸い四角形を描画する TCanvas.RoundRectメソッドがありますが,GDI+ には同様のメソッドは実装されていません.リージョンあるいはパスを使用すると描画できます.
GDI+ はどちらかと言うとパスの機能が充実しています.以下はパスを使用する例です.


図3
設計時画面
  • 描画用コントロールは実行時生成
図4
実行結果
  • [Button1] をクリックすると塗り潰しの色が変更できる

本サンプルでは円弧のパスの作成順番を右回りで作成しています.
パスを作成すると追加順に結合されていきます.TGPGraphicsPath.CloseFigure メソッドを実行すると,最初に作成したパスと最後に作成したパスが結合して閉じた図形になります.したがって,パスの作成順序が異なると,下図のように期待する結果とならない場合があります.


図5
パスの作成順序に注意
  • 左上の円弧のパスの次に右下の円弧のパスを作成した場合

左回り (反時計回り) でパスを追加していく場合は以下のように書けます.
GDI+ の角度はデフォルトでは度単位で,時計回りの方向が正の値です.左回りの反時計方向は負の値になりますから,描画範囲の角度を負の値にします.

  //左上
  LGPPath.AddArc(LLeft, LTop, LSize, LSize, 270, -90);
  //左下
  LGPPath.AddArc(LLeft, LTop + LHeight - LSize, LSize, LSize, 180, -90);
  //右下
  LGPPath.AddArc(LLeft + LWidth - LSize, LTop + LHeight - LSize, LSize, LSize, 90, -90);
  //右上
  LGPPath.AddArc(LLeft + LWidth - LSize, LTop, LSize, LSize, 0, -90);


リスト2
GDI+ による角が丸い四角形の描画
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  protected
    procedure Paint; override;
  public
    FBrushColor : Cardinal;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    ColorDialog1: TColorDialog;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption := 'GDI+ 角が丸い四角形の描画';

  //TMyPanelを生成して配置
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent  := Self;
    Left    := 5;
    Top     := 35;
    Width   := Self.ClientWidth  - 10;
    Height  := Self.ClientHeight - Top - 5;
    Anchors := [akLeft, akTop, akRight, akBottom];
    Color   := clWhite;
    ParentBackground := False;
    DoubleBuffered   := True;

    FBrushColor := aclYellow;
  end;
end;

//=============================================================================
//  ブラシの色を変更する
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  ColorDialog1.Color := ARGBToColorRef(MyPanel.FBrushColor);
  if ColorDialog1.Execute then begin
    MyPanel.FBrushColor := ColorRefToARGB(ColorDialog1.Color);
  end;
  MyPanel.Invalidate;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//  角が丸い四角形の例
//  本サンプルでは矩形そのものは描画しない.作成したパスだけを描画する
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LGPGraphic : TGPGraphics;
  LGPPath    : TGPGraphicsPath;
 LGPPen     : TGPPen;
  LGPBrush   : TGPSolidBrush;
  LLeft      : Integer;
  LTop       : Integer;
  LWidth     : Integer;
  LHeight    : Integer;
  LPenWidth  : Integer;
  LSize      : Integer;
begin
  inherited;

  LLeft     := 20;
  LTop      := 20;
  LWidth    := 230;
  LHeight   := 130;
  LPenWidth := 3;
  LSize     := 32;


  //ペンのオブジェクトを生成
  LGPPen := TGPPen.Create(aclBlack, LPenWidth);
  //TGPSolidBrushを生成
  LGPBrush := TGPSolidBrush.Create(FBrushColor);

  //TGPGraphicsオブジェクトを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //---------------------------------------------------------------------------
  //  パスの作成開始
  //  矩形領域の位置とサイズの値を元に円弧のパスを作成
  //  パスの追加はTGPGraphicsPath.AddXXX
  //---------------------------------------------------------------------------
  LGPPath := TGPGraphicsPath.Create;

  //パスに円弧を追加していく(指定直径の1/4の円)
  //左上
  LGPPath.AddArc(LLeft, LTop, LSize, LSize, 180, 90);
  //右上
  LGPPath.AddArc(LLeft + LWidth - LSize, LTop, LSize, LSize, 270, 90);
  //右下
  LGPPath.AddArc(LLeft + LWidth - LSize, LTop + LHeight - LSize, LSize, LSize, 0, 90);
  //左下
  LGPPath.AddArc(LLeft, LTop + LHeight - LSize, LSize, LSize, 90, 90);

  //作成したパスを結合して閉じた図形にする
  LGPPath.CloseFigure();
  //---------------------------------------------------------------------------
  //  パスの作成終了
  //---------------------------------------------------------------------------

  //パスの内部を塗り潰す
  //塗り潰す必要がなければ実行する必要はない(ブラシの設定も不要)
  LGPGraphic.FillPath(LGPBrush, LGPPath);

  //パスの輪郭を描画
  //パスや線の描画関係の関数名はDrawXXXX
  LGPGraphic.DrawPath(LGPPen, LGPPath);


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

end.




03_GDI+ によるバルーンヒントウィンドウのような図形


前項のサンプルコードを少し変更して,パスに三角形を追加すると,下図のようなバルーンヒントウィンドウのような図形が描画できます.
実用的にするには,どの変数をプロパティにするとか,矢印としての三角形の描画位置と矩形の描画位置の関係をどうするかを考える必要があります.少し面倒かも知れません.


図6
実行結果

リスト3
GDI+ によるバルーンヒントウィンドウのような図形
矢印の始点とは,矢印の先端部の座標値.終点は矩形と接続する部分の座標値
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  protected
    procedure Paint; override;
  public
    FBrushColor : Cardinal;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    ColorDialog1: TColorDialog;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption := 'GDI+ バルーンヒントウィンドウ';

  //TMyPanelを生成して配置
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent  := Self;
    Left    := 5;
    Top     := 35;
    Width   := Self.ClientWidth  - 10;
    Height  := Self.ClientHeight - Top - 5;
    Anchors := [akLeft, akTop, akRight, akBottom];
    Color   := clWhite;
    ParentBackground := False;
    DoubleBuffered   := True;

    FBrushColor := aclYellow;
  end;
end;

//=============================================================================
//  ブラシの色を変更する
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  ColorDialog1.Color := ARGBToColorRef(MyPanel.FBrushColor);
  if ColorDialog1.Execute then begin
    MyPanel.FBrushColor := ColorRefToARGB(ColorDialog1.Color);
  end;
  MyPanel.Invalidate;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//  バルーンヒントウィンドウのような図形
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LGPGraphic   : TGPGraphics;
  LGPPath      : TGPGraphicsPath;
 LGPPen       : TGPPen;
  LGPBrush     : TGPSolidBrush;
  LLeft        : Integer;
  LTop         : Integer;
  LWidth       : Integer;
  LHeight      : Integer;
  LPenWidth    : Integer;
  LSize        : Integer;
  LArrowStartX : Integer;
  LArrowStartY : Integer;
  LArrowEndX   : Integer;
  LArrowEndY   : Integer;
  LArrowHeight : Integer;
  LArrowWidth  : Integer;
  LArrowShift  : Integer;
  LPoints      : array of TGPPoint;
begin
  inherited;

  LPenWidth := 3;

  LArrowHeight := 70;
  LArrowWidth  := 30;
  LArrowShift  := 25;

  LArrowStartX := 70;
  LArrowStartY := 200;
  LArrowEndX   := LArrowStartX + LArrowShift;
  LArrowEndY   := LArrowStartY - LArrowHeight;

  //矩形のサイズと位置の設定
  LSize   := 32;
  LWidth  := 200;
  LHeight := 100;
  LLeft   := (LArrowEndX - LArrowWidth div  2) - LSize;
  LTop    := LArrowEndY - LHeight;


  //ペンのオブジェクトを生成
  LGPPen := TGPPen.Create(aclBlack, LPenWidth);
  //TGPSolidBrushを生成
  LGPBrush := TGPSolidBrush.Create(FBrushColor);

  //TGPGraphicsオブジェクトを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //---------------------------------------------------------------------------
  //  パスの作成開始
  //  以下のコードでは次の値を使用
  //    矢印の始点と終点のX,Y座標値
  //    矢印の幅(三角形の底辺の寸法)
  //    矩形の位置とサイズ
  //
  //  パスの追加はTGPGraphicsPath.AddXXX
  //---------------------------------------------------------------------------
  LGPPath := TGPGraphicsPath.Create;

  //矢印部分の直線を描く座標を設定
  SetLength(LPoints, 3);
  LPoints[1] := MakePoint(LArrowStartX, LArrowStartY);
  if (LArrowEndX = LLeft) or (LArrowEndX = LLeft + LWidth) then begin
    LPoints[0] := MakePoint(LArrowEndX, LArrowEndY + LArrowWidth div 2);
    LPoints[2] := MakePoint(LArrowEndX, LArrowEndY - LArrowWidth div 2);
  end else begin
    LPoints[0] := MakePoint(LArrowEndX + LArrowWidth div 2, LArrowEndY);
    LPoints[2] := MakePoint(LArrowEndX - LArrowWidth div 2, LArrowEndY);
  end;

  //パスに円弧を追加していく(指定直径の1/4の円)
  //左上
  LGPPath.AddArc(LLeft, LTop, LSize, LSize, 180, 90);
  if (LArrowEndY = LTop) then begin
    LGPPath.AddLine(LPoints[2], LPoints[1]);
    LGPPath.AddLine(LPoints[1], LPoints[0]);
  end;

  //右上
  LGPPath.AddArc(LLeft + LWidth - LSize, LTop, LSize, LSize, 270, 90);
  if (LArrowEndX = LLeft + LWidth) then begin
    LGPPath.AddLine(LPoints[2], LPoints[1]);
    LGPPath.AddLine(LPoints[1], LPoints[0]);
  end;

  //右下
  LGPPath.AddArc(LLeft + LWidth - LSize, LTop + LHeight - LSize, LSize, LSize, 0, 90);
  if (LArrowEndY = LTop + LHeight) then begin
    LGPPath.AddLine(LPoints[0], LPoints[1]);
    LGPPath.AddLine(LPoints[1], LPoints[2]);
  end;

  //左下
  LGPPath.AddArc(LLeft, LTop + LHeight - LSize, LSize, LSize, 90, 90);
  if (LArrowEndX = LLeft) then begin
    LGPPath.AddLine(LPoints[0], LPoints[1]);
    LGPPath.AddLine(LPoints[1], LPoints[2]);
  end;

  //作成したパスを結合して閉じた図形にする
  LGPPath.CloseFigure();
  //---------------------------------------------------------------------------
  //  パスの作成終了
  //---------------------------------------------------------------------------

  //パスの内部を塗り潰す
  //塗り潰す必要がなければ実行する必要はない(ブラシの設定も不要)
  LGPGraphic.FillPath(LGPBrush, LGPPath);

  //パスの輪郭を描画
  //パスや線の描画関係の関数名はDrawXXXX
  LGPGraphic.DrawPath(LGPPen, LGPPath);


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

end.


上のコードは矩形の下側に矢印を描画する例です.
以下は上のコードの動作確認に使用した設定値の例です.これらの設定は,矩形の位置とサイズを決定してから矢印の位置を設定するか,その逆,あるいは別の設定手順があるかも知れません.


//----------------------------------------------------------------------------
//  矩形の上に矢印を描画する例
//----------------------------------------------------------------------------

  LArrowHeight := 70;
  LArrowWidth  := 30;
  LArrowShift  := 20;

  LArrowStartX := 70;
  LArrowStartY := 50;
  LArrowEndX   := LArrowStartX + LArrowShift;
  LArrowEndY   := LArrowStartY + LArrowHeight;

  //矩形のサイズと位置の設定
  LSize   := 32;
  LWidth  := 200;
  LHeight := 100;
  LLeft   := (LArrowEndX - LArrowWidth div  2) - LSize;
  LTop    := LArrowEndY;

//----------------------------------------------------------------------------
//  矩形の左側に矢印を描画する例
//----------------------------------------------------------------------------

  LArrowHeight := 50;
  LArrowWidth  := 20;
  LArrowShift  := 0;

  LArrowStartX := 60;
  LArrowStartY := 100;
  LArrowEndX   := LArrowStartX + LArrowHeight;
  LArrowEndY   := LArrowStartY - LArrowShift;

  //矩形のサイズと位置の設定
  LSize   := 32;
  LWidth  := 200;
  LHeight := 100;
  LLeft   := LArrowEndX;
  LTop    := LArrowEndY - 50;

//----------------------------------------------------------------------------
//  矩形の右側に矢印を描画する例
//----------------------------------------------------------------------------

  LArrowHeight := 50;
  LArrowWidth  := 20;
  LArrowShift  := 0;

  LArrowStartX := 300;
  LArrowStartY := 100;
  LArrowEndX   := LArrowStartX - LArrowHeight;
  LArrowEndY   := LArrowStartY - LArrowShift;

  //矩形のサイズと位置の設定
  LSize   := 32;
  LWidth  := 200;
  LHeight := 100;
  LLeft   := LArrowEndX - LWidth;
  LTop    := LArrowEndY - 50;

//----------------------------------------------------------------------------