Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2012年12月06日] [更新 2012年12月06日] GDI+ 関係サンプル
   
GDI+ 関係サンプル 
G060_フォントと文字列
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
G060_GDIPlus_FontAndString.zip [6,788 KB] 2012年12月06日版 (EXE 同梱)







GDI+ のフォント設定と文字列の描画


GDI+ の機能を使用して文字列を描画する際の参考サンプルです.
同じコントロール内で,GDI+ の関数類を使用した描画と,VCL の TCanvas への文字列画像のコードを混在させると,描画が揺れることがあります.その場合は,GDI+ の文字列描画用の関数類を使用することになるでしょう.





01_文字列の描画  -  DrawString, UnitPixel, UnitPoint


文字列は,TGPGraphics.DrawString 関数を使用します.
フォントは,TGPFont オブジェクトのインスタンスを生成して設定します.
文字の色は,VCL では TFont.Color ですが,GDI+ では TGPBrush を使用します.したがって,いろいろな効果を持たせることができます.

これまでのサンプルでは,描画の位置とサイズの単位として,主に,UnitPixel という,画面のピクセル単位を使用していました.文字列を,画像や図形内に文字列を描画する際,ピクセル単位で指定すれは,描画位置の設定に便利です.

本ページでは,UnitPoint という単位も使用しています.この UnitPixcel は TFont.Size と同じポイント数で設定するものです.UnitPoint の単位は 1/72インチです.
UnitPixel での描画は,文字列の場合,TFont.Height に相当しますが,同じではありません.GDI+ ではフォントサイズに,負の値は指定できません.

次のサンプルでは,この描画単位の効果を確認するために,両方の単位で文字列を描画しています.描画単位の変更に,SetPageUnit 関数を使用しています.
比較として,VCL の TCanvas.Font を使用した文字列も描画しています.



図1
設計時画面
図2
実行結果
  • どの文字列もフォントサイズは 20
  • 一番上は,ピクセル単位
  • 下の 2 つはポイント数の指定となる

リスト1
文字列描画の基本テスト
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  protected
    procedure Paint; override;
  end;

  TForm1 = class(TForm)
    procedure FormCreate(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, 5, Self.ClientWidth - 10, Self.ClientHeight - 10);
    DoubleBuffered   := True;
    ControlStyle     := MyPanel.ControlStyle + [csOpaque];
    BevelOuter       := bvNone;
    Visible          := True;
    Color            := clSkyBlue;
    ParentBackground := False;
    Anchors          := [akLeft, akTop, akRight, akBottom];
  end;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の文字列の描画コード
//
//  文字列の描画は,フォント(TGPFont)とブラシ(TGPBrush)のオブジェクトを生成する
//  TGPFontは,フォント名,フォントサイズ,フォントスタイル等を設定する
//  TGPBrushは,文字の色をブラシとして設定する
//
//  フォントは
//  LGPFont := TGPFont.Create(Canvas.Handle, HFONT);
//  として,第2引数に描画先のCAnvasのLogFont構造体の値を指定することも可能
//
//  UnitPixel は,ピクセル単位での文字サイズの設定
//  UnitPointは,TFont.sizeと同じく,ポイント数で文字のサイズを設定
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LGPGraphic : TGPGraphics;
  LGPFont    : TGPFont;
  LGPBrush   : TGPSolidBrush;
  LFontSize  : Single;
begin
  inherited;

  LFontSize := 20.0;

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

  //描画先をこのTMyPanelのCanvasに指定して,TGPGraphicsのオブジェクトを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);
  //そのCanvasに文字列を描画
  LGPGraphic.DrawString('UnitPixel', -1, LGPFont, MakePoint(0.0, 0.0), LGPBrush);

  //文字のサイズをポイント数で指定して文字列を描画
  LGPGraphic.SetPageUnit(UnitPoint);
  LGPGraphic.DrawString('UnitPoint', -1, LGPFont, MakePoint(0.0, 30.0), LGPBrush);

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


  //比較のため,VCLのTCanvasを使用して文字列を描画
  Canvas.Font.Size := Trunc(LFontSize);
  Canvas.Font.Name := 'Arial';
  Canvas.TextOut(0, 80, 'UnitPoint  VCL TextOut');
end;

end.




02_ブラシ効果の文字列


文字の色には,TGPPathGradientBrush 以外のブラシが使用できます.
次のサンプルは,リニアグラデーションを使用した例です.


図3
設計時画面
図4
実行結果

ブラシの適用範囲が,実際の文字列の描画範囲より少ないと,下図のようになってしまいます.


図5
ブラシの適用範囲に注意

リスト2
グラデーション付きの文字列
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  protected
    procedure Paint; override;
  end;

  TForm1 = class(TForm)
    procedure FormCreate(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, 5, Self.ClientWidth - 10, Self.ClientHeight - 10);
    DoubleBuffered   := True;
    ControlStyle     := MyPanel.ControlStyle + [csOpaque];
    BevelOuter       := bvNone;
    Visible          := True;
    Color            := clWhite;
    ParentBackground := False;
    Anchors          := [akLeft, akTop, akRight, akBottom];
  end;
end;

{ TMyPanel }

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

  LFontSize := 80.0;

  //TGPFontとTGPSolidBrushのオブジェクトを生成し設定
  //太字のイタリック体とし,文字のサイズをポイント数で指定
  LGPFont  := TGPFont.Create('Arial', LFontSize, FontStyleBoldItalic, UnitPoint);
  //リニアグラデーションを設定
  LGPBrush := TGPLinearGradientBrush.Create(MakeRect(0, 10, 400, 50),
                                            MakeColor(255, 0, 255, 0),
                                            MakeColor(255, 0, 0,   255),
                                            LinearGradientModeHorizontal);

  //描画先をこのTMyPanelのCanvasに指定して,TGPGraphicsのオブジェクトを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);
  //そのCanvasに文字列を描画
  LGPGraphic.DrawString('Delphi', -1, LGPFont, MakePoint(0.0, 10.0), LGPBrush);

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

end.




03_GDI+ の描画内容をプリンタへ出力 - UnitMillimeter


描画の単位を UnitMillimeter にすると,ミリメートル単位での描画となります.
プリンタへ出力する際,この単位で描画すれば,単位の換算が必要ありません.画面上でも,この単位でのサイズに換算して表示します.この描画単位の設定は,文字列だけでなく,GDI+ の描画全てに共通です.

次のサンプルは,この単位で描画し,プリンタへ出力します.画面への描画とプリンタへの出力の両方で描画コードを共有するために,描画先の TCanvas を引数にした DrawRoutine というメソッドを実装しています.



図6
設計時画面
図7
実行画面
図8
[印刷] ボタンをクリック

リスト3
GDI+ の描画内容をプリンタへ出力
描画コードは,画面と印刷用と同じ.違うのは出力先の Canvas
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
    procedure DrawRoutine(ACanvas: TCanvas);
  end;

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

var
  Form1: TForm1;

implementation

uses 
  Printers;

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption := 'GDI+ の描画をプリンタへ出力';

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

//=============================================================================
//  [印刷]ボタン
//  TMyPanelのDrawRoutineメソッドを実行して,描画コードをTPrinter.Canvasに出力
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;

  if PrintDialog1.Execute then begin
    Printer.BeginDoc;
    try
      MyPanel.DrawRoutine(Printer.Canvas);
    finally
      Printer.EndDoc;
    end;
  end;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コードはDrawRoutineメソッドで行う
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
begin
  inherited;

  DrawRoutine(Canvas);
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//  引数のACanvasは描画先のTCanvas
//  TPrinter.Canvasにすればプリンタへの出力となる
//-----------------------------------------------------------------------------
procedure TMyPanel.DrawRoutine(ACanvas: TCanvas);
var
  LGPGraphic : TGPGraphics;
  LGPFont    : TGPFont;
  LGPBrush   : TGPLinearGradientBrush;
  LFontSize  : Single;
begin
  inherited;

  LFontSize := 27.0;

  //TGPFontとTGPSolidBrushのオブジェクトを生成し設定
  //太字のイタリック体とし,文字のサイズをミリメートル単位で指定
  LGPFont  := TGPFont.Create('Arial', LFontSize, FontStyleBoldItalic, UnitMillimeter);
  //リニアグラデーションを設定
  LGPBrush := TGPLinearGradientBrush.Create(MakeRect(0, 10, 400, 50),
                                            aclRed,
                                            aclRoyalBlue,
                                            LinearGradientModeHorizontal);

  //描画先を引数のCanvasに指定して,TGPGraphicsのオブジェクトを生成
  LGPGraphic := TGPGraphics.Create(ACanvas.Handle);
  //そのCanvasに文字列を描画
  LGPGraphic.DrawString('Delphi', -1, LGPFont, MakePoint(10.0, 10.0), LGPBrush);

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

end.




04_書式指定の文字列   -  縦書き


書式を指定して文字列を描画するには,TGPStringFormat オブジェクトのインスタンスを生成して,その関数,SetFormatFlags で設定します.
次のサンプルは,縦書きを指定して,改行後の文字列を左側に描画する例です.
題材は JUNKI さんのサイトのものをお借りしました.


表1 
書式設定用関数,SetFormatFlags で指定可能な値.複数のフラグの指定が可能

StringFormatFlagsDirectionRightToLeft 改行後の文字の描画位置を現在の左側とする
StringFormatFlagsDirectionVertical 縦方向に描画する
StringFormatFlagsNoFitBlackBox 英小文字の f 等のオーバーハングがある文字の扱い方のようであるが,効果は確認できず
StringFormatFlagsDisplayFormatControl 制御文字の出力
StringFormatFlagsNoFontFallback 指定フォントにない文字を代替フォントで表示
デフォルトでは,指定フォントにない文字は,白い四角の文字を使用する
StringFormatFlagsMeasureTrailingSpaces 文字列末尾の空白 (半角・全角共)も文字列に含める
これを指定しないと,末尾の空白はアライメントの際に無視される
StringFormatFlagsNoWrap 領域指定の文字列描画の際,自動改行を無効にする
StringFormatFlagsLineLimit 複数行表示の際の設定
これを設定すると,文字本体が完全に矩形領域内に描画できない場合は,その行の文字列を描画しない
StringFormatFlagsNoClip MSDN の記事からは,描画領域をクリップしないで描画すると解釈できるが,今回は縦方向のみ確認できた
グリフ部の描画関係の設定ではないかと思われる


図9
設計時画面
図10
実行画面
  • 背景に半透明の画像を表示してみた

リスト4
縦書きの文章
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  private
    FStartPoint : TGPPointF;
    FFontName   : string;
    FFontSize   : Single;
    FFontColor  : Cardinal;
    FText       : string;
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
  end;

  TForm1 = class(TForm)
    procedure FormCreate(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, 5, Self.ClientWidth - 10, Self.ClientHeight - 10);
    DoubleBuffered   := True;
    ControlStyle     := MyPanel.ControlStyle + [csOpaque];
    BevelOuter       := bvNone;
    Visible          := True;
    Color            := clWhite;
    ParentBackground := False;
    Anchors          := [akLeft, akTop, akRight, akBottom];
  end;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタ
//  背景に描画する画像用のビットマップ生成
//  文字列関係のプロパティを設定
//  HG行書体はMicrosoft Officeに含まれている
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;

  FStartPoint := MakePoint(220.0, 15.0);
  FFontName   := 'HG行書体';
  FFontSize   := 30;
  FFontColor  := MakeColor(255, 0, 0, 0);
  FText       := '潮かをる北の浜辺の' + sLineBreak +
                 '砂山のかの浜薔薇よ' + sLineBreak +
                 '今年も咲けるや'     + sLineBreak +
                 '        石川啄木';
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//
//  StringFormatFlagsDirectionVerticalで縦書き指定
//  StringFormatFlagsDirectionRightToLeftで改行後の次の行が左側となる
//  描画開始位置は,右上となるので注意
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LBitmap       : TBitmap;
  LGPGraphic    : TGPGraphics;
  LGPFontFamily : TGPFontFamily;
  LGPStrFormat  : TGPStringFormat;
  LGPFont       : TGPFont;
  LGPBrush      : TGPSolidBrush;
begin
  inherited;

  //サンプルの趣旨と関係ないが,背景に画像を表示してみる
  LBitmap := TBitmap.Create;
  try
    LBitmap.SetSize(Width, Height);
    LBitmap.LoadFromFile('Hachi.bmp');
    LBitmap.Canvas.StretchDraw(ClientRect, LBitmap);
    Canvas.Draw(0, 0, LBitmap, 80);
  finally
    FreeAndNil(LBitmap);
  end;


  //フォント名とフォントサイズ等を設定
  LGPFontFamily := TGPFontfamily.Create(FFontName);
  LGPFont       := TGPFont.Create(LGPFontFamily,
                                  FFontSize,
                                  FontStyleRegular,
                                  UnitPixel);

  //TGPStringFormatを生成して,縦書きの設定
  LGPStrFormat := TGPStringFormat.Create();
  LGPStrFormat.SetFormatFlags(StringFormatFlagsDirectionVertical or
                              StringFormatFlagsDirectionRightToLeft);

  //文字のブラシ(色)を設定
  LGPBrush := TGPSolidBrush.Create(FFontColor);

  //描画先をこのコントロールのCanvasにしてTGPGraphicsを生成して描画
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);
  LGPGraphic.DrawString(FText, -1, LGPFont, FStartPoint, LGPStrFormat, LGPBrush);

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

end.




05_書式指定の文字列  -  アライメント


右寄せ等,描画のアライメントの設定は,同じく TGPStringFormat の SetAlignment 関数で指定します.下表は指定可能な値です.


表2 
アライメントの指定用定数値

StringAlignmentNear 左端または上端寄せ
StringAlignmentCenter 中央
StringAlignmentFar 右端または下端寄せ


図11
設計時画面
図12
実行時画面
  • 左寄せで上端寄せ
図13
上下左右共に中央の場合
図14
右寄せで上下の中央の場合

文字列の末尾に半角または全角の空白文字が含まれている場合,空白文字を描画領域内に描画するかを指定することができます.
SetFormatFlags 関数の引数に,StringFormatFlagsMeasureTrailingSpaces を含めると,この空白分も描画します.このフラグの指定がない場合,空白分は描画しません.デフォルトでは,このフラグは含まれていません.


図15
空白文字も描画

次の図は,日本語を描画したものです.上の結果の図でもそうですが,英文字では,上に少し余白があります.


図16
日本語と英文字の比較

かって印刷は,活字と言われる金属片の 1つ 1 の文字を組み合わせて行われていました.これを活版印刷と呼んでいます.
この文字,タイポグラフィの単位として,下図のような,em-square というのが使われています.これらの用語は,1440 年代のヨハン・グーテンベルクの時代から,いまでもフォントの用語に使用されています.1 em-square はポイントサイズと同じ幅の空白を意味します.

英文字は,外部レディングを除いた内部に描画されますが,日本語は em-squre 内に描画されます.つまり,日本語は,同じポイント数でも,英文字よりも大きいサイズとなります.この方が全体的にバランスが良くなることから,一般的に使用されています.





リスト5
アライメントを指定した文字列の描画
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  protected
    procedure Paint; override;
  end;

  TForm1 = class(TForm)
    RadioGroup1: TRadioGroup;
    RadioGroup2: TRadioGroup;
    CheckBox1: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure RadioGroup1Click(Sender: TObject);
    procedure RadioGroup2Click(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
  i : Integer;
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;

  //アライメントの選択項目を作成
  for i := 0 to 2 do begin
    RadioGroup1.Items.Add(GetEnumName(TypeInfo(TStringAlignment), i));
  end;
  RadioGroup2.Items.Text := RadioGroup1.Items.Text;

  RadioGroup1.ItemIndex := 0;
  RadioGroup2.ItemIndex := 0;

  //文字列末尾の空白もアライメントの対象にする設定
  CheckBox1.Caption := 'StringFormatFlagsMeasureTrailingSpaces';
end;

//=============================================================================
//  RadioGroup1のOnClickイベント
//  選択したら,MyPanel1を再描画
//=============================================================================
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.Invalidate;
end;

//=============================================================================
//  RadioGroup2のOnClickイベント
//  選択したら,MyPanel1を再描画
//=============================================================================
procedure TForm1.RadioGroup2Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.Invalidate;
end;

//=============================================================================
//  ChekcBoox1のOnClickイベント
//  操作したら,MyPanel1を再描画
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.Invalidate;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//
//  Form1の値を参照しているが,本来はTMyPanelのプロパティにすべき
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LGPGraphic   : TGPGraphics;
  LGPFont      : TGPFont;
  LGPStrFormat : TGPStringFormat;
  LGPBrush     : TGPSolidBrush;
  LGPPen       : TGPPen;
  LGPRect      : TGPRectF;
begin
  inherited;

  //描画範囲の矩形領域を設定
  LGPRect := MakeRect(30.0, 20.0, 250.0, 100.0);

  //TGPFontのオブジェクトを生成し設定
  //太字とし,文字のサイズをポイント数で指定
  LGPFont := TGPFont.Create('Arial', 30, FontStyleBold, UnitPoint);

  //TGPStringFormatを生成してアライメントを設定
  LGPStrFormat := TGPStringFormat.Create;
  //横方向のアライメント
  LGPStrFormat.SetAlignment(TStringAlignment(Form1.RadioGroup1.ItemIndex));
  //横方向のアライメント
  LGPStrFormat.SetLineAlignment(TStringAlignment(Form1.RadioGroup2.ItemIndex));

  //文字列の末尾の空白文字の扱い
  if Form1.CheckBox1.Checked then begin
    LGPStrFormat.SetFormatFlags(StringFormatFlagsMeasureTrailingSpaces or
                                StringFormatFlagsNoFitBlackBox);
  end;

  //ブラシで文字の色を設定
  LGPBrush := TGPSolidBrush.Create(MakeColor(255, 0, 0, 0));

  //確認用の矩形を描画するペンを設定
  LGPPen := TGPPen.Create(aclBlack);

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

  //描画領域を示す矩形を描画
  LGPGraphic.DrawRectangle(LGPPen, LGPRect);

  //Canvasに文字列を描画
  LGPGraphic.DrawString('Delphi ', -1, LGPFont, LGPRect, LGPStrFormat, LGPBrush);

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

end.




06_領域を指定した描画 - 複数行表示


改行文字で区切られた複数行の文字列も,書式の設定が使えます.そのテストプログラムです.
複数行の場合,StringFormatFlagsNoWrap を指定すると,折り返し表示をしなくなります.


図17
設計時画面
図18
デフォルトの設定の場合
図19
左右の中央に描画
図20
折り返し表示なし

下図は,フォントを「メイリオ」した場合です.


図21
「メイリオ」にした場合

リスト6
複数行の文字列のアライメント設定のテストプログラム
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  private
    FStringList : TStringList;
  protected
    procedure Paint; override;
    procedure SetParent(AParent: TWinControl); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

  TForm1 = class(TForm)
    RadioGroup1: TRadioGroup;
    RadioGroup2: TRadioGroup;
    CheckBox1: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure RadioGroup1Click(Sender: TObject);
    procedure RadioGroup2Click(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
  i : Integer;
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;

  //アライメントの選択項目を作成
  for i := 0 to 2 do begin
    RadioGroup1.Items.Add(GetEnumName(TypeInfo(TStringAlignment), i));
  end;
  RadioGroup2.Items.Text := RadioGroup1.Items.Text;

  RadioGroup1.ItemIndex := 0;
  RadioGroup2.ItemIndex := 0;

  //自動改行の設定
  CheckBox1.Caption := 'StringFormatFlagsNoWrap';
end;

//=============================================================================
//  RadioGroup1のOnClickイベント
//  選択したら,MyPanel1を再描画
//=============================================================================
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.Invalidate;
end;

//=============================================================================
//  RadioGroup2のOnClickイベント
//  選択したら,MyPanel1を再描画
//=============================================================================
procedure TForm1.RadioGroup2Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.Invalidate;
end;

//=============================================================================
//  ChekcBoox1のOnClickイベント
//  操作したら,MyPanel1を再描画
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.Invalidate;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタ
//  ディスクから読み出すファイルのテキストを格納するTStringListを生成
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FStringList := TStringList.Create;
end;

//-----------------------------------------------------------------------------
//  デストラクタ
//-----------------------------------------------------------------------------
destructor TMyPanel.Destroy;
begin
  FreeAndNil(FStringList);
  inherited;
end;

//-----------------------------------------------------------------------------
//  SetParentメソッド
//  ディスクからテキストファイルを読み出す
//-----------------------------------------------------------------------------
procedure TMyPanel.SetParent(AParent: TWinControl);
begin
  inherited;
  if not Assigned(FStringList) then exit;

  FStringList.LoadFromFile('AAA.txt');
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//
//  Form1の値を参照しているが,本来はTMyPanelのプロパティにすべき
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LGPGraphic : TGPGraphics;
  LGPFont    : TGPFont;
  LGPStrFmt  : TGPStringFormat;
  LGPBrush   : TGPSolidBrush;
  LGPPen     : TGPPen;
  LGPRect    : TGPRectF;
begin
  inherited;

  //描画範囲の矩形領域を設定
  LGPRect := MakeRect(20.0, 20.0, 250.0, 150.0);

  //TGPFontのオブジェクトを生成し設定
  //文字のサイズをポイント数で指定
  LGPFont := TGPFont.Create('MS Pゴシック', 10, FontStyleRegular, UnitPoint);

  //TGPStringFormatを生成してアライメントを設定
  LGPStrFmt := TGPStringFormat.Create;
  //横方向のアライメント
  LGPStrFmt.SetAlignment(TStringAlignment(Form1.RadioGroup1.ItemIndex));
  //横方向のアライメント
  LGPStrFmt.SetLineAlignment(TStringAlignment(Form1.RadioGroup2.ItemIndex));

  //自動改行の設定
  if Form1.CheckBox1.Checked then begin
    LGPStrFmt.SetFormatFlags(StringFormatFlagsNoWrap);
  end;

  //ブラシで文字の色を設定
  LGPBrush := TGPSolidBrush.Create(MakeColor(255, 0, 0, 0));

  //確認用の矩形を描画するペンを設定
  LGPPen := TGPPen.Create(aclBlack);

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

  //描画領域を示す矩形を描画
  LGPGraphic.DrawRectangle(LGPPen, LGPRect);

  //Canvasにディスクから読み出したテキストを描画
  LGPGraphic.DrawString(FStringList.Text, -1, LGPFont, LGPRect, LGPStrFmt, LGPBrush);

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

end.




07_インストールされているフォント情報の取得


システムにインストールされているフォントの一覧を取得するサンプルです.
TGPInstalledFontCollection の GetFamilies 関数を使用します.


図22
設計時画面
  • TListBox を配置
図23
実行画面
  • TListBox のフォント名をクリックすると,そのフォントでフォント名の文字列を描画する
  • 環境によっては,描画しないフォントがある

リスト7
システムにインストールされているフォント情報を取得するサンプル
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  private
    FpFontFamily : array of TGPFontFamily;
    FFontNumber  : Integer;
    FFontCount   : Integer;
    FFontList    : TStringList;
    procedure SetFontNumber(const Value: Integer);
  protected
    procedure Paint; override;
    procedure SetParent(AParent: TWinControl); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property FontNumber : Integer read FFontNumber write SetFontNumber;
    property FontCount  : Integer read FFontCount;
    property FontList   : TStringList read FFontList;
  end;

  TForm1 = class(TForm)
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
  private
    { Private 宣言 }
    MyPanel : TMyPanel;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption := 'GDI+  インストール済みのフォント';

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

  ListBox1.Items := MyPanel.FontList;
  ListBox1.ItemIndex := 0;
end;

//=============================================================================
//  ListBox1のOnClickイベント
//  ListBox1で選択したフォントを表示する
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.FontNumber := ListBox1.ItemIndex;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コントラクタ
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;

  SetLength(FpFontFamily, 0);
  FFontCount  := 0;
  FFontNumber := -1;
  FFontList   := TStringList.Create;
end;

//-----------------------------------------------------------------------------
//  デコントラクタ
//-----------------------------------------------------------------------------
destructor TMyPanel.Destroy;
var
  i : Integer;
begin
  //Createしたオブジェクトは解放処理する
  FreeAndNil(FFontList);
  for i := 0 to Length(FpFontFamily) - 1 do begin
    FpFontFamily[i].Free;
  end;

  inherited;
end;

//-----------------------------------------------------------------------------
//  SetParentメソッド
//  親コントロールが設定されたら,フォントの一覧を取得し,属性を配列に格納
//-----------------------------------------------------------------------------
procedure TMyPanel.SetParent(AParent: TWinControl);
var
  LInstallFontCollection : TGPInstalledFontCollection;
  LCount     : Integer;
  LFontCount : Integer;
  LFontName  : string;
  i          : Integer;
begin
  inherited;
  if not Assigned(AParent) then exit;
  //既に取得済みなら取得のコードを実行しない
  if FFontCount > 0 then exit;


  //システムにインストールされているフォントコレクションのオブジェクトを生成
  LInstallFontCollection := TGPInstalledFontCollection.Create;
  //インストールされてるいるフォント数を取得
  LCount := LInstallFontCollection.GetFamilyCount;

  //情報格納用の配列のサイズを設定
  SetLength(FpFontFamily, LCount);

  //配列の各要素にTGPFontFamilyオブジェクトを生成
  for i := 0 to LCount - 1 do begin
    FpFontFamily[i] := TGPFontFamily.Create;
  end;
  //配列に全てのフォント情報を格納する
  LFontCount := 0;
  LInstallFontCollection.GetFamilies(LCount, FpFontFamily, LFontCount);

  //FontListプロパティにフォント名を格納
  for i := 0 to LCount - 1 do begin
    FpFontFamily[i].GetFamilyName(LFontName);
    FFontList.Add(LFontName);
  end;
  //FFontCountプロパティの値の設定と,表示するフォント番号を設定
  FFontCount  := LCount;
  FFontNumber := 0;

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

//-----------------------------------------------------------------------------
//  FontNumaberプロパティの設定用メソッド
//-----------------------------------------------------------------------------
procedure TMyPanel.SetFontNumber(const Value: Integer);
var
  LFlag : Boolean;
begin
  if FFontCount = 0 then exit;

  LFlag := (Value >=0) and (Value <= (FFontCount - 1));
  if LFlag and (FFontNumber <> Value) then begin
    FFontNumber := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//  FFontNumber番目のフォントで,そのフォント名を描画
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LGPGraphic   : TGPGraphics;
  LGPFont      : TGPFont;
  LGPStrFormat : TGPStringFormat;
  LGPBrush     : TGPSolidBrush;
  LGPDrawRect  : TGPRectF;
  LFontName    : string;
begin
  inherited;
  if FFontCount = 0 then exit;

  //描画の矩形領域を設定
  LGPDrawRect := MakeRect(20.0, 20.0, 250.0, 150.0);

  //TGPFontのオブジェクトを生成し設定
  //第1引数は,取得しておいたTGPFontFamilyのFFontNumber番目の値
  //フォントスタイルは指定なし.サイズをポイント数で指定
  LGPFont := TGPFont.Create(FpFontFamily[FFontNumber],
                            30,
                            FontStyleRegular,
                            UnitPoint);
  //フォント名を取得する
  FpFontFamily[FFontNumber].GetFamilyName(LFontName);

  //TGPStringFormatを生成する
  //特に何も設定しないでデフォルトで使用
  LGPStrFormat := TGPStringFormat.Create;

  //ブラシで文字の色を設定
  LGPBrush := TGPSolidBrush.Create(MakeColor(255, 0, 0, 0));

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

  //そのCanvasにフォント名を描画
  LGPGraphic.DrawString(LFontName,
                        - 1,
                        LGPFont,
                        MakePoint(10.0, 10.0),
                        LGPStrFormat,
                        LGPBrush);

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

end.




08_アンチエイリアシング  -  SetTextRenderingHint


GDI+ のオブジェクトに対する操作の内,アンチエイリアシングやスムージングに関する関数には,オブジェクトに応じて,次の表のものがあります.
コミュニティの場では,「滑らかにしたい」とか「ギザギサ (ジャギー)」とか表現されている状態を改善するための関数ということになります.
文字列は,実際にはレンダリング関係の処理の設定のようですが,AntiAlias を含むフラグを指定すると,アンチエイリアシング処理を行ってくれます.この機能を確認するテストです.


表3 
GDI+ のアンチエイリアス (スムージング) 処理関係の関数

オブジェクト 関数 関数の引数の型
画像 TGPGraphic.SetInterpolationMode TInterpolationMode
図形 TGPGraphics.SetSmoothingMode TSmoothingMode
文字列 TGPGraphic.SetTextRenderingHint TTextRenderingHint


表4 
TextRenderingHint 列挙型の値.SetTextRenderingHint 関数の引数に,この値を指定する

TextRenderingHintSystemDefault
TextRenderingHintSingleBitPerPixelGridFit
TextRenderingHintSingleBitPerPixel
TextRenderingHintAntiAliasGridFit
TextRenderingHintAntiAlias
TextRenderingHintClearTypeGridFit


図24
設計時画面
図25
実行時画面
  • 左はデフォルトの設定での描画
  • 右は AntiAlias
  • GDI+ ではデフォルトのままでもある程度のアンチエイリアス処理をする

リスト8
アンチエイリアシング処理
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  protected
    procedure Paint; override;
  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

{$R *.DFM}

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

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

  RadioGroup1.Items.CommaText := 'TextRenderingHintSingleBitPerPixelGridFit,' +
                                 'TextRenderingHintSingleBitPerPixel,'        +
                                 'TextRenderingHintAntiAliasGridFit,'         +
                                 'TextRenderingHintAntiAlias,'                +
                                 'TextRenderingHintClearTypeGridFit';
  RadioGroup1.ItemIndex := 0;
end;

//=============================================================================
//  RadioGroup1のOnClickイベント処理
//=============================================================================
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.Invalidate;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//
//  Form1の値を参照しているが,本来はTMyPanelのプロパティにすべき
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LText      : string;
  LFontSize  : Single;
  LGPGraphic : TGPGraphics;
  LGPFont    : TGPFont;
  LGPBrush   : TGPSolidBrush;
  LRenderNo  : TTextRenderingHint;
begin
  inherited;

  LText  := '山';
  LFontSize := 140;

  //TGPFontとTGPSolidBrushのオブジェクトを生成し設定
  LGPFont  := TGPFont.Create('HG行書体', LFontSize, FontStyleRegular, UnitPixel);
  LGPBrush := TGPSolidBrush.Create(aclBlack);

  //描画先をこのTMyPanelのCanvasに指定して,TGPGraphicsのオブジェクトを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);
  //そのCanvasに文字列を描画
  LGPGraphic.SetTextRenderingHint(TextRenderingHintSystemDefault);
  LGPGraphic.DrawString(LText, - 1, LGPFont, MakePoint(0.0, 0.0), LGPBrush);

  //レンダリングの設定
  //...AntiAilas...にすればアンチエイリアスになる
  case Form1.RadioGroup1.ItemIndex of
    0: LRenderNo := TextRenderingHintSingleBitPerPixelGridFit;
    1: LRenderNo := TextRenderingHintSingleBitPerPixel;
    2: LRenderNo := TextRenderingHintAntiAliasGridFit;
    3: LRenderNo := TextRenderingHintAntiAlias;
    4: LRenderNo := TextRenderingHintClearTypeGridFit;
  end;

  //レンダリングした文字列を描画
  LGPGraphic.SetTextRenderingHint(LRenderNo);
  LGPGraphic.DrawString(LText, -1, LGPFont, MakePoint(LFontSize, 0.0), LGPBrush);

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

end.




09_文字列の幅と高さ  -  MeasureString


文字列の幅と高さを取得するサンプルです.
VCL では,TCanvas.TextWidth,TCanvas.TextHeight で取得できますが,GDI+ では MeasureString 関数を使用します.ただし,描画領域が正しく取得できるのは,DrawString の関数の文字列書式の設定として,TGPStringFormat.GenericDefault を指定した場合だけです.

このフラグは,左端寄せ,上端寄せでの描画です.したがって,複数行の文字列描画で,他の書式を設定して描画した文字列では正確に取得できません.

更に詳細に描画領域を求めるには,TGPFontFamily オブジェクトに

の関数があります.これらが使えるかも知れません.


図26
設計時画面
図27
GenericDefault の場合
図28
GenericTypographic の場合

リスト9
文字列の幅と高さ
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  protected
    procedure Paint; override;
  private
    procedure DrawText(AStrText: String; APoint: TGPPointF;
      AFontSize: Single; AFontStyle: FontStyle);
  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

{$R *.DFM}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption := 'GDI+  文字列の幅と高さの取得';

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

  //取得する属性の設定
  RadioGroup1.Items.CommaText := 'TGPStringFormat.GenericDefault,' +
                                 'TGPStringFormat.GenericTypographic';
  RadioGroup1.ItemIndex := 0;
end;

//=============================================================================
//  RadioGroup1のOnClickイベント
//=============================================================================
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.Invalidate;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コードこの下のDrawTextで実行する
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LStartPoint : TGPPointF;
  LFontSize   : Single;
begin
  inherited;

  LFontSize   := 30.0;

  //フォントスタイルは通常で英大文字
  LStartPoint := MakePoint(20.0, 20.0);
  DrawText('DELPHI', LStartPoint, LFontSize, FontStyleRegular);

  //フォントスタイルは通常で英小文字
  LStartPoint := MakePoint(20.0, 20.0 + (LFontSize + 20.0));
  DrawText('font size', LStartPoint, LFontSize, FontStyleRegular);

  //フォントスタイルは通常で漢字
  LStartPoint := MakePoint(20.0, 20.0 + (LFontSize + 20.0) * 2);
  DrawText('文字列', LStartPoint, LFontSize, FontStyleRegular);

  //斜体フォントスで英大文字
  LStartPoint := MakePoint(20.0, 20.0 + (LFontSize + 20.0) * 3);
  DrawText('DELPHI', LStartPoint, LFontSize, FontStyleItalic);

  //斜体フォントスで英小文字
  LStartPoint := MakePoint(20.0, 20.0 + (LFontSize + 20.0) * 4);
  DrawText('font size', LStartPoint, LFontSize, FontStyleItalic);

  //斜体フォントスで漢字
  LStartPoint := MakePoint(20.0, 20.0 + (LFontSize + 20.0) * 5);
  DrawText('文字列', LStartPoint, LFontSize, FontStyleItalic);
end;

//-----------------------------------------------------------------------------
//  実際の描画コードを実行するメソッド
//-----------------------------------------------------------------------------
procedure TMyPanel.DrawText(AStrText: String; APoint: TGPPointF;
  AFontSize: Single; AFontStyle: FontStyle);
var
  LSizeRectF : TGPRectF;
  LGPGraphic : TGPGraphics;
  LGPFont    : TGPFont;
  LGPBrush   : TGPSolidBrush;
  LGPPen     : TGPPen;
begin
  inherited;

  //TGPFontとTGPSolidBrushのオブジェクトを生成し設定
  //フォントのサイズとスタイルは引数の値を使用
  LGPFont  := TGPFont.Create('Times New Roman', AFontSize, AFontStyle, UnitPixel);
  LGPBrush := TGPSolidBrush.Create(aclBlack);

  //描画先をこのTMyPanelのCanvasに指定して,TGPGraphicsのオブジェクトを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);
  //文字列を描画する前に,描画領域のサイズを取得
  LGPGraphic.MeasureString(AStrText, -1, LGPFont, APoint, LSizeRectF);

  //矩形を描画するペンのオブジェクトを生成して領域を囲む矩形を描画
  LGPPen := TGPPen.Create(aclBlack, 1);
  LGPGraphic.DrawRectangle(LGPPen, LSizeRectF);

  //Canvasに文字列を描画
  //描画する文字列,描画開始点,文字サイズ(ピクセル)は引数の値を使用
  case Form1.RadioGroup1.ItemIndex of
    0:
    begin
      LGPGraphic.DrawString(AStrText,
                             -1,
                             LGPFont,
                             APoint,
                             TGPStringFormat.GenericDefault,
                             LGPBrush);
    end;
    1:
    begin
      LGPGraphic.DrawString(AStrText,
                             -1,
                             LGPFont,
                             APoint,
                             TGPStringFormat.GenericTypographic,
                             LGPBrush);
    end;
  end;

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

end.




10_パス文字列や図形,画像の幅と高さ  -  GetBounds


同じ文字列でも,パスにした文字列は図形です.図形の描画領域は,GetBounds 関数で取得できます.画像も取得できますが,画像の場合は,表示のサイズではなく,元の画像のサイズとなります.
このサンプルでは,枠線がパスの図形に隠れないように,パスを描画した後に領域の矩形を描画していますが,矩形領域は,図形を描く前に取得できます.

GDI+ で文字列や図形をパスにするには,

  • TGPGraphicsPath のオブジェクトを生成
  • TGPGraphicsPath.AddXXX の名前の関数で,文字列や図形を描画
  • TGPGraphics.FillPath でパスの内部を塗潰す (塗潰す必要がない場合は不要)
  • TGPGraohics.DrawPath で輪郭線を描画 (輪郭線を描かなければ不要)
  • 必要であれば, TGPGraphicsPath.AddPath でパスを結合したパスを作成
という手順でコーディングします.AddXXX の描画関数には,円,楕円,ベジェ曲線等,TCanvas で使用可能な関数と同じものが用意されています.BeginPath,EndPath で構成するよりも扱いやすいと言えるでしょう.
ただし,引数の種類に注意が必要です.例えば,文字列の AddString 関数の場合,TGPFontFamily と TGPStringFormat のオブジェクトを必ず使用します.


図29
設計時画面
図30
実行画面
  • 黒い線が図形の描画領域

リスト10
パス文字列の幅と高さの取得には GetBounds 関数を使用する
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  protected
    procedure Paint; override;
  end;

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

var
  Form1: TForm1;

implementation

{$R *.DFM}

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

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

{ TMyPanel }

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//  2つのパス文字列を作成して表示
//  作成したパス図形の領域の枠線を描画
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LSizeRectF    : TGPRectF;
  LStrText      : String;
  LFontSize     : Single;
  LGPGraphic    : TGPGraphics;
  LGPBrush      : TGPSolidBrush;
  LGPFontFamily : TGPFontFamily;
  LGPStrFormat  : TGPStringFormat;
  LGPPath       : TGPGraphicsPath;
  LGPPen        : TGPPen;
begin
  inherited;

  LStrText  := 'DELPHI';
  LFontSize := 60;

  //パス内部を塗潰すBrushを生成
  LGPBrush := TGPSolidBrush.Create(aclSkyBlue);

  //AddStringの引数に必要なTGPFontFamilyとTGPStringFormatを生成
  LGPFontFamily := TGPFontFamily.Create('Arial Black');
  LGPStrFormat  := TGPStringFormat.Create();

  //パス作成開始
  LGPPath := TGPGraphicsPath.Create;
  //パスに文字列を追加
  LGPPath.AddString(LStrText,
                    - 1,
                    LGPFontFamily,
                    FontStyleRegular,
                    LFontSize,
                    MakePoint(10.0, 10.0),
                    LGPStrFormat);

  //描画先をこのTMyPanelのCanvasに指定して,TGPGraphicsのオブジェクトを生成
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);
 //バス内部を塗潰す
  LGPGraphic.FillPath(LGPBrush, LGPPath);

  //パスの全体のサイズを取得
  LGPPath.GetBounds(LSizeRectF);
  //図形の取り囲む線を描画するためのペンを生成
 LGPPen := TGPPen.Create(aclBlack, 1);
  //パス全体を囲む矩形を描画
  LGPGraphic.DrawRectangle(LGPPen, LSizeRectF);


  //次のパスを作成する準備
  //上と違うのは,斜体文字とし,位置を下にずらしただけ
  LGPPath.Free;
  LGPPath := TGPGraphicsPath.Create;
  LGPPath.AddString(LStrText,
                    - 1,
                    LGPFontFamily,
                    FontStyleItalic,
                    LFontSize,
                    MakePoint(10.0, 90.0),
                    LGPStrFormat);
 LGPGraphic.FillPath(LGPBrush, LGPPath);

  LGPPath.GetBounds(LSizeRectF);
  LGPGraphic.DrawRectangle(LGPPen, LSizeRectF);


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

end.




11_エンボス文字列


JUNKI さんのところにサンプルがあります.筆者も真似してやってみました.
視覚的な効果は,背景の色等も影響するようです.


図31
実行結果

リスト11
エンボス文字列のようなもの
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  protected
    procedure Paint; override;
  end;

  TForm1 = class(TForm)
    procedure FormCreate(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, 5, Self.ClientWidth - 10, Self.ClientHeight - 10);
    DoubleBuffered   := True;
    ControlStyle     := MyPanel.ControlStyle + [csOpaque];
    BevelOuter       := bvNone;
    Visible          := True;
    Color            := $00D0D0D0;
    ParentBackground := False;
    Anchors          := [akLeft, akTop, akRight, akBottom];
  end;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LStrText   : String;
  LGPGraphic : TGPGraphics;
  LGPFont    : TGPFont;
  LGPBrush   : TGPSolidBrush;
begin
  inherited;

  LStrText := 'Delphi';

  //TGFontとTGPSolidBrushのオブジェクトを生成し設定
  LGPFont  := TGPFont.Create('Times New Roman', 55, FontStyleBold, UnitPoint);
  LGPBrush := TGPSolidBrush.Create(MakeColor(255, 0, 0, 0));

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

  //そのCanvasに文字列を描画
  LGPBrush.SetColor(aclWhite);
  LGPGraphic.DrawString(LStrText, - 1, LGPFont, MakePoint(0.0, 0.0), LGPBrush);
  LGPBrush.SetColor(aclDarkGray);
  LGPGraphic.DrawString(LStrText, - 1, LGPFont, MakePoint(4.0, 4.0), LGPBrush);
  LGPBrush.SetColor(aclBlack);
  LGPGraphic.DrawString(LStrText, - 1, LGPFont, MakePoint(2.0, 2.0), LGPBrush);

  LGPBrush.SetColor(aclBlack);
  LGPGraphic.DrawString(LStrText, - 1, LGPFont, MakePoint(0.0, 90.0), LGPBrush);
  LGPBrush.SetColor(aclWhite);
  LGPGraphic.DrawString(LStrText, - 1, LGPFont, MakePoint(4.0, 92.0), LGPBrush);
  LGPBrush.SetColor(aclGray);
  LGPGraphic.DrawString(LStrText, - 1, LGPFont, MakePoint(2.0, 91.0), LGPBrush);

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

end.