Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2012年11月23日] [更新 2017年07月27日] GDI+ 関係サンプル
GDI+ 関係サンプル 
G022_カラーマトリックスによる画像の色操作
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
G022_GDIPlus_ColorMatrix.zip [6,360 KB] 2017年07月27日版 (EXE 同梱)







GDI+ のカラーマトリックス


このページのサンプルも,GDI+ のテストプログラムです.
「移動リサイズコントロール」とは直接関係ありませんが,画像を扱う際の参考用です.

GDI+ では,色に対してカラーマトリックス (カラー行例,色行列) を使用することにより,画像の各ピクセルに対して各種の操作を行うことができます.
この行列は,各ピクセルの,赤 (R),緑 (G),青 (B),アルファ値 (α) に対して,以下の線形演算を実行します.
  
このマトリックスの各要素の値は,0 から 1.0 の実数値を使用します.0.0 は輝度がないか透明であることを意味し,1.0 は輝度が最大か完全な不透明であることを意味します.要素の値は実数ですが,実際の画素,ピクセルに適用される値は 0 から 255 の範囲となります.
行列の [5, 5] の値は常に 1.0 とします.第 5 例目のその他の要素は 常に 0.0 とします.
  • 第 1 列目は赤 (R) の要素に対する設定
  • 第 2 列目は緑 (G) の要素に対する設定
  • 第 3 列目は青 (B) の要素に対する設定
  • 第 4 列目はアルファ値対する設定
  • 第 5 列目はダミーの列
となっています.同様に各行は,例えば第 1 列目の,
  • 第 1 行目は赤 (R) のピクセルの赤 (R) の割合
  • 第 2 行目は赤 (R) のピクセルの緑 (G) の割合
  • 第 3 行目は赤 (R) のピクセルの青 (B) の割合
  • 第 4 行目は赤 (R) のピクセルのアルファ値の割合
  • 第 5 行目は赤 (R) のピクセルの色の平行移動量の割合
となっています.第 5 行目は色の移動,平行移動のアフィイン変換 (線形変換に続いて平行移動を実行する変換) を可能にしています.マトリックスの行数と列数を同じにするために,第 5 列目をダミーとして追加しています.これによって線形変換と平行移動を任意に組み合わせた変換を実行することができます.
このカラーマトリックスの初期値は,以下の単位行列としますが,TColorMatrix の変数を定義してもこの値とはなりません.利用者が単位行列に初期化する必要があります.
    
以下は,これカラーマトリックスを使用した簡単なテストプログラムです.
カラーマトリックスは,TGPImageAttributes のオブジェクトを生成して,SetColorMatrix 関数でセットして使用します.


リスト1
カラーマトリックスは,TGPImageAttributes のオブジェクトを生成して,
その TGPImageAttributes の SetColorMatrix 関数でセットする
var
  LGPGraohc  : TGPGraphics;
  LGPImgAttr : TGPImageAttributes;
  ......
begin
  LGPImgAttr := TGPImageAttributes.Create;
  LGPImgAttr.SetColorMatrix(ColorMatrix);
  ......
  ......


リスト2
カラーマトリックスの初期化操作.単位行列を作成する例
  //カラーマトリックスの初期化(単位行列を作成)
  FillChar(ColorMatrix, SizeOf(TColorMatrix), #0);
  for i := 0 to 4 do begin
    ColorMatrix[i, i] := 1.0;
  end;


TGPImageAttributes には,このページで紹介している以外にも次のような関数があります.

関数名 機能
SetRemapTable 指定した色を,別の指定した色に置き換える
SetColorKey 透過色を範囲で指定し,その範囲内の色を透過描画する
SetOutputChannel CMYK (cyan, magenta, yellow, black) 変換
SetThreshold 色の描画閾値の設定
SetWrapMode 描画先サイズがイメージサイズより大きな場合のラップ (回り込み) モード
画像描画の際の終端ピクセルの補間処理の指定



[備考]
サンプルの記事とは直線関係ありませんが,FireMonkey フレームワークでは,多くの画像処理関係の機能が実装されています.
FireMonkeyでは,2D 関係のグラフィックス処理は,Direct2D が使用されます.Direct2D が使用できない環境では GDI+ が使用される仕様になっています.RAD Studio 関係のオンラインヘルプも画像付きとなり,筆者が Delphi でプログラミングを始めた頃と較べると,隔世の感があります.




01_カラーマトリックスによる単純な演算


このサンプルでは,あらかじめ値をセットしたカラーマトリックスを,RadioGroup で選択して,そのカラーマトリックスを使用して色を操作します.


図1
設計時画面
図2
オリジナル画像

次図は,次のように,カラーマトリックスの緑の要素だけを 1 とした場合です.
[緑以外は非描画]は,この数値を -1 にする場合ですが,筆者の Windows 7 では 1 の時と同じ結果となりました.Windows XP では描画しない効果がありました.
   


図3
青以外の輝度が 0 の場合

次の図は,赤,緑,青の輝度の要素の値を,次のように -1 にした場合の結果です.
色が反転して,ネガ画像となります.
   


図4
色が反転してネガになる

リスト3
単純なカラーマトリックスでの色操作例
unit Unit1;

interface

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

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;
  protected

  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := 'GDI+  カラーマトリックス 単純な演算';

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

    ParentBackground := False;
    DoubleBuffered   := True;
  end;

  //RadioGrou1に選択項目を作成して表示
  RadioGroup1.Items.Add('オリジナル');
  RadioGroup1.Items.Add('赤以外は非描画');
  RadioGroup1.Items.Add('赤以外は輝度 0');
  RadioGroup1.Items.Add('緑以外は非描画');
  RadioGroup1.Items.Add('緑以外は輝度 0');
  RadioGroup1.Items.Add('青以外は非描画');
  RadioGroup1.Items.Add('青以外は輝度 0');
  RadioGroup1.Items.Add('反転 (ネガ)');

  //初期選択項目
  RadioGroup1.ItemIndex := 0;
end;

//=============================================================================
//  RadioGroup1のOnClickイベン
//  RadioGroup1の選択項目を変更したら,MyPanelを再描画
//=============================================================================
procedure TForm1.RadioGroup1Click(Sender: TObject);
begin
  MyPanel.Invalidate;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//
//  このPaintメソッドのコードだけをコピペしてのテストも可能
//
//  (1) 新規プロジェクトを作成
//  (2) usesにGDIPOBJ, GDIPAPIに追加
//  (3) Form1のOnPaintイベントハンドラを作成(コードは空のまま)
//  (4) このPaintメソッドのコードを,そのイベントハンドラに貼り付け
//  (5) inherited; の行を削除(実際には何もしないのであってもかまわない
//  (6) コンパイルするとエラーが発生するので修正
//  (7) このエラーは,ほとんどプロパティ関係なので,変数のかわりに数値等を書く
//
//  他の,似たような構成のサンプルコードは,全て同じようにテスト可能
//  そのために,クラス構成のTMyPanelを定義したサンプルにしている
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;

  //---------------------------------------------------------------------------
  //  いろいろなカラーマトリックスを用意しておく
  //  RadioGroup1の選択項目によって使用するマトリックスを変える
  //---------------------------------------------------------------------------
const
  //オリジナル
  ColorMatrix_Origin : TColorMatrix = (
    ( 1.0,  0.0,  0.0,  0.0,  0.0),
    ( 0.0,  1.0,  0.0,  0.0,  0.0),
    ( 0.0,  0.0,  1.0,  0.0,  0.0),
    ( 0.0,  0.0,  0.0,  1.0,  0.0),
    ( 0.0,  0.0,  0.0,  0.0,  1.0));

  //赤以外は非描画
  ColorMatrix_OnlyRed : TColorMatrix = (
    ( 1.0,  0.0,  0.0,  0.0,  0.0),
    ( 0.0, -1.0,  0.0,  0.0,  0.0),
    ( 0.0,  0.0, -1.0,  0.0,  0.0),
    ( 0.0,  0.0,  0.0,  1.0,  0.0),
    ( 0.0,  0.0,  0.0,  0.0,  1.0));

  //赤以外は輝度 0
  ColorMatrix_Red : TColorMatrix = (
    ( 1.0,  0.0,  0.0,  0.0,  0.0),
    ( 0.0,  0.0,  0.0,  0.0,  0.0),
    ( 0.0,  0.0,  0.0,  0.0,  0.0),
    ( 0.0,  0.0,  0.0,  1.0,  0.0),
    ( 0.0,  0.0,  0.0,  0.0,  1.0));

  //緑以外は非描画
  ColorMatrix_OnlyGreen : TColorMatrix = (
    (-1.0,  0.0,  0.0,  0.0,  0.0),
    ( 0.0,  1.0,  0.0,  0.0,  0.0),
    ( 0.0,  0.0, -1.0,  0.0,  0.0),
    ( 0.0,  0.0,  0.0,  1.0,  0.0),
    ( 0.0,  0.0,  0.0,  0.0,  1.0));

  //緑以外は輝度 0
  ColorMatrix_Green : TColorMatrix = (
    ( 0.0,  0.0,  0.0,  0.0,  0.0),
    ( 0.0,  1.0,  0.0,  0.0,  0.0),
    ( 0.0,  0.0,  0.0,  0.0,  0.0),
    ( 0.0,  0.0,  0.0,  1.0,  0.0),
    ( 0.0,  0.0,  0.0,  0.0,  1.0));

  //青以外は非描画
  ColorMatrix_OnlyBlue : TColorMatrix = (
    (-1.0,  0.0,  0.0,  0.0,  0.0),
    ( 0.0, -1.0,  0.0,  0.0,  0.0),
    ( 0.0,  0.0,  1.0,  0.0,  0.0),
    ( 0.0,  0.0,  0.0,  1.0,  0.0),
    ( 0.0,  0.0,  0.0,  0.0,  1.0));

  //青以外は輝度 0
  ColorMatrix_Blue : TColorMatrix = (
    ( 0.0,  0.0,  0.0,  0.0,  0.0),
    ( 0.0,  0.0,  0.0,  0.0,  0.0),
    ( 0.0,  0.0,  1.0,  0.0,  0.0),
    ( 0.0,  0.0,  0.0,  1.0,  0.0),
    ( 0.0,  0.0,  0.0,  0.0,  1.0));

  //色を反転させた画像(ネガティブイメージ)
  ColorMatrix_Invers : TColorMatrix = (
    (-1.0,  0.0,  0.0,  0.0,  0.0),
    ( 0.0, -1.0,  0.0,  0.0,  0.0),
    ( 0.0,  0.0, -1.0,  0.0,  0.0),
    ( 0.0,  0.0,  0.0,  1.0,  0.0),
    ( 1.0,  1.0,  1.0,  1.0,  1.0));

  //---  const部終わり  -------------------------------------------------------

var
  LGPGraphic   : TGPGraphics;
  LGPImage     : TGPImage;
  LGPImageAttr : TGPImageAttributes;
begin
  inherited;

  //画像ファイルを指定して,TGPImageのオブジェクトを生成
  LGPImage := TGPImage.Create('dogs018.jpg');

  //TGPImageAttributesのオブジェクトを生成
  LGPImageAttr := TGPImageAttributes.Create;
  //RadioGroup1の選択項目によって,使用するマトリックスを変える
  case Form1.RadioGroup1.ItemIndex of
    0: LGPImageAttr.SetColorMatrix(ColorMatrix_Origin);
    1: LGPImageAttr.SetColorMatrix(ColorMatrix_OnlyRed);
    2: LGPImageAttr.SetColorMatrix(ColorMatrix_Red);
    3: LGPImageAttr.SetColorMatrix(ColorMatrix_OnlyGreen);
    4: LGPImageAttr.SetColorMatrix(ColorMatrix_Green);
    5: LGPImageAttr.SetColorMatrix(ColorMatrix_OnlyBlue);
    6: LGPImageAttr.SetColorMatrix(ColorMatrix_Blue);
    7: LGPImageAttr.SetColorMatrix(ColorMatrix_Invers);
  end;

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

  //GPImageをこのTMyPanelのCanvasに描画
  //TCanvasのStretchDrawに相当する
  LGPGraphic.DrawImage(
              LGPImage,
              MakeRect(0, 0, ClientWidth, ClientHeight),
              0,
              0,
              LGPImage.GetWidth,
              LGPImage.GetHeight,
              UnitPixel,
              LGPImageAttr);

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

end.




02_半透明画像


半透明画像,つまりアルファブレンドな画像にするには,4 行 4 列目の値を 0 から 1 までの任意の実数値にします.0 は完全な透明,1 は完全な不透明になります.実際に画素に適用されるのは 0 から 255 の範囲となります.
  


図5
設計時画面
図6
オリジナル画像
図7
半透明にした状態

リスト4
半透明処理
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  private
    FImageFilePath : String;
    FAlphaValue    : Single;
    procedure SetAlphaValue(const Value: Single);
    procedure SetImageFilePath(const Value: String);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    property ImageFilePath : String read FImageFilePath write SetImageFilePath;
    property AlphaValue    : Single read FAlphaValue    write SetAlphaValue;
  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
  Self.Caption := 'GDI+  カラーマトリックス 半透明画像';

  //透明度設定用
  TrackBar1.ShowSelRange := False;
  TrackBar1.Height   := 23;
  TrackBar1.Min      := 0;
  TrackBar1.Max      := 100;
  TrackBar1.Position := TrackBar1.Max;

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

    ParentBackground := False;
    DoubleBuffered   := True;

    //TMyPanel固有のプロパティの指定
    //表示する画像ファイル名と画像の透明度
    ImageFilePath := 'dogs018.jpg';
    AlphaValue    := 1.00;
  end;
end;

//=============================================================================
//  TrackBar1のOnChangeイベント処理
//  アルファ値(透明度)を変更したら,TMyPanelクラスのAlphaValueの値を更新する
//  カラーマトリックスで指定するアルファ値の範囲は 0 - 1.00 であることに注意 
//=============================================================================
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;
  MyPanel.AlphaValue := TrackBar1.Position / 100;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタのCreate処理
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FAlphaValue := 1.00;
end;

//-----------------------------------------------------------------------------
//  AlphaValueプロパティの設定用メソッド
//  画像の透明度であるアルファ値を変更したら,TMyPanelを再描画
//  GDI+ のアルファ値の範囲は 0 - 1.00 
//-----------------------------------------------------------------------------
procedure TMyPanel.SetAlphaValue(const Value: Single);
begin
  if FAlphaValue <> Value then begin
    FAlphaValue := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  ImageFilePathプロパティの設定用メソッド
//  表示する画像を変更したらTMyPanelを再描画
//-----------------------------------------------------------------------------
procedure TMyPanel.SetImageFilePath(const Value: String);
begin
  FImageFilePath := Value;
  Invalidate;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LColorMatrix : TColorMatrix;
  LGPGraphic   : TGPGraphics;
  LGPImage     : TGPImage;
  LGPImageAttr : TGPImageAttributes;
  i            : Integer;
begin
  inherited;

  //半透明の確認用に,背景に文字列を描画
  Canvas.Font.Size  := 50;
  Canvas.Font.Name  := 'MS UI Gothic';
  Canvas.Font.Style := [fsBold];
  Canvas.TextOut(20, 30, 'DELPHI');

  //カラーマトリックスの初期化(単位行列を作成)
  FillChar(LColorMatrix,SizeOf(TColorMatrix), #0);
  for i := 0 to 4 do begin
    LColorMatrix[i,i] := 1.0;
  end;
  //アルファ値を設定
  LColorMatrix[3, 3] := FAlphaValue;

  //画像ファイルを指定してTGPImageのオブジェクトを生成
  LGPImage := TGPImage.Create(FImageFilePath);

  //TGPImageAttributesのオブジェクトを生成してカラーマトリックスをセット
  LGPImageAttr := TGPImageAttributes.Create;
  LGPImageAttr.SetColorMatrix(LColorMatrix);

  //TGPGraphicsのオブジェクトを生成.Canvasを描画先とする
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //GPImageをこのCanvasに描画
  //TCanvasのStretchDrawに相当する
  LGPGraphic.DrawImage(
              LGPImage,
              MakeRect(0, 0, ClientWidth, ClientHeight),
              0,
              0,
              LGPImage.GetWidth,
              LGPImage.GetHeight,
              UnitPixel,
              LGPImageAttr);

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

end.


カラーマトリックスによる半透明処理は,非常に時間がかかります.
半透明の処理だけであれば,次のように描画先を TGPBitmap にして,そのビットマップのピクセルを直接操作してしまう方が高速です.


//----------------------------------------------------------------------------
//  TGPBitmapの半透明化処理
//  アルファチャンネルに半透明の値を設定する
//
//  SrcGDPBmpのビット深さが32bit以上用
//  GDI+のTGPImage, TGPBitmapの保持ビット深さは32bit
//----------------------------------------------------------------------------
function SetOpacitAlphaValue(SrcGDPBmp: TGPBitmap; PixelFmt: TPixelFormat;
  Opacity: Byte): Boolean;
var
  LBmpData    : TBitmapData;
  LScanLine   : PByte;
  LpRGBQuad   : PRGBQuad;
  LWidth      : Integer;
  LHeight     : Integer;
  LDataStride : Integer;
  LXPos       : integer;
  LYPos       : Integer;
begin
  Result := False;

  LWidth  := SrcGDPBmp.GetWidth;
  LHeight := SrcGDPBmp.GetHeight;

  if SrcGDPBmp.LockBits(MakeRect(0, 0, LWidth, LHeight),
                        ImageLockModeWrite,
                        PixelFmt, LBmpData) <> Ok then exit;

  LScanLine   := LBmpData.Scan0;
  LDataStride := LBmpData.Stride;

  for LYPos := 0 to LHeight - 1 do begin
    LpRGBQuad := PRGBQuad(LScanLine);
    for LXPos := 0 to LWidth - 1 do begin
      LpRGBQuad^.rgbReserved := Opacity;
      Inc(LpRGBQuad);
    end;
    Inc(LScanLine, LDataStride);
  end;

  Result := True;
  SrcGDPBmp.UnlockBits(LBmpData);
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LGPGraphic : TGPGraphics;
  LGDIBitmap : TGPBitmap;
begin
  inherited;

  //半透明の確認用に,背景に文字列を描画
  Canvas.Font.Size  := 50;
  Canvas.Font.Name  := 'MS UI Gothic';
  Canvas.Font.Style := [fsBold];
  Canvas.TextOut(20, 30, 'DELPHI');

  //TGPBitmapのインスタンスを生成
  LGDIBitmap := TGPBitmap.Create(FImageFilePath);

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

  //半透明化処理
  SetOpacitAlphaValue(LGDIBitmap, PixelFormat32bppARGB, Trunc(FAlphaValue * 255));

  //その描画先にTGPBitmapの内容を描画
  LGPGraphic.DrawImage(LGDIBitmap,
                       MakeRect(0, 0, Width, Height),
                       0,
                       0,
                       LGDIBitmap.GetWidth,
                       LGDIBitmap.GetHeight,
                       UnitPixel);

  LGPGraphic.Free;
  LGDIBitmap.Free;
end;




03_モノクロ画像への変換 ( グレースケール化 )


カラー画像をモノクロ画像,グレースケール化するには,次のように,カラーマトリックスの各色における R, G, B の要素の値を平均して与える方法が考えられます.

しかし,この設定は実際には使われていません.人の目は,色の違いによって明るさが違って見えるからです.下の図は,左から ( 255  0  0 ) ( 0  255 0 ) ( 0  0  255 ) の色です.これを見ると緑色が一番明るく見えます.


これをそのままの輝度でモノクロの画像に変換すると,次のようになります.この色による輝度の違いは,最初のサンプルでも確認できます.

この,色による視覚的な輝度の違いを考慮して,NTSC (National Television Standards Committee)では,カラー画像を白黒に変換する際に,輝度 Y を以下の式で変換するように規格を定めています.
    Y = R × 0.298912 + G × 0.586611 + B × 0.114478
この式を,カラーマトリックスに適用すると,次のようになります.
   
このサンプルプログラムは,カラーマトリックスに,この値をセットしてグレースケールにしています.透明度も設定できるようにしてみました.AlphaValue がその値です.


図8
設計時画面
図9
実行画面
図10
アルファ値を変更

リスト5
カラー画像をグレースケール化
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  private
    FImageFilePath : String;
    FAlphaValue    : Single;
    procedure SetImageFilePath(const Value: String);
    procedure SetAlphaValue(const Value: Single);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    property ImageFilePath : String read FImageFilePath write SetImageFilePath;
    property AlphaValue    : Single read FAlphaValue    write SetAlphaValue;
  end;

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

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := 'GDI+  カラーマトリックス グレースケール化';

  TrackBar1.ShowSelRange := False;
  TrackBar1.Height   := 23;
  TrackBar1.Min      := 0;
  TrackBar1.Max      := 100;
  TrackBar1.Position := TrackBar1.Max;
  TrackBar1.Anchors  := [akLeft, akBottom];

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

    ParentBackground := False;
    DoubleBuffered := True;

    //TMyPanel固有のプロパティの指定
    //表示する画像ファイル名と画像の透明度
    AlphaValue    := 1.00;
    ImageFilePath := 'dogs018.jpg';
  end;
end;

//=============================================================================
//  TrackBar1のOnChangeイベント処理
//  アルファ値(透明度)を変更したら,TMyPanelクラスのAlphaValueの値を更新する
//  GDI+ のアルファ値の範囲は 0 - 1.00 であることに注意 
//=============================================================================
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;

  MyPanel.AlphaValue := TrackBar1.Position / 100;
  MyPanel.Invalidate;
end;

//=============================================================================
//  CheckBox1のOnChangeイベント処理
//  TMyPanelを再描画する
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
  MyPanel.Invalidate;
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタのCreate処理
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FAlphaValue := 1.00;
end;

//-----------------------------------------------------------------------------
//  AlphaValueプロパティの設定用メソッド
//  画像の透明度であるアルファ値を変更したら,TMyPanelを再描画
//  GDI+ のアルファ値の範囲は 0 - 1.00 
//-----------------------------------------------------------------------------
procedure TMyPanel.SetAlphaValue(const Value: Single);
begin
  if FAlphaValue <> Value then begin
    FAlphaValue := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  ImageFilePathプロパティの設定用メソッド
//  表示する画像を変更したらTMyPanelを再描画
//-----------------------------------------------------------------------------
procedure TMyPanel.SetImageFilePath(const Value: String);
begin
  FImageFilePath := Value;
  Invalidate;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LColorMatrix : TColorMatrix;
  LGPGraphic   : TGPGraphics;
  LGPImage     : TGPImage;
  LGPImageAttr : TGPImageAttributes;
  LRValue      : Single;
  LGValue      : Single;
  LBValue      : Single;
  i            : Integer;
begin
  inherited;

  if Form1.CheckBox1.Checked then begin
    LRValue := 1 / 3;
    LGValue := 1 / 3;
    LBValue := 1 / 3;
  end else begin
    LRValue := 0.298912;
    LGValue := 0.586611;
    LBValue := 0.114478;
  end;

  //カラーマトリックスの初期化(単位行列を作成)
  FillChar(LColorMatrix,SizeOf(TColorMatrix), #0);
  for i := 0 to 4 do begin
    LColorMatrix[i,i] := 1.0;
  end;

  //各要素の値を代入
  LColorMatrix[0, 0] := LRValue;
  LColorMatrix[0, 1] := LRValue;
  LColorMatrix[0, 2] := LRValue;

  LColorMatrix[1, 0] := LGValue;
  LColorMatrix[1, 1] := LGValue;
  LColorMatrix[1, 2] := LGValue;

  LColorMatrix[2, 0] := LBValue;
  LColorMatrix[2, 1] := LBValue;
  LColorMatrix[2, 2] := LBValue;

  //アルファ値を設定
  LColorMatrix[3, 3] := FAlphaValue;

  //TGPImageAttributesのオブジェクトを生成してカラーマトリックスをセット
  LGPImageAttr := TGPImageAttributes.Create;
  LGPImageAttr.SetColorMatrix(LColorMatrix);

  //画像ファイルを指定してTGPImageのオブジェクトを生成
  LGPImage := TGPImage.Create(FImageFilePath);

  //TGPGraphicsのオブジェクトを生成.Canvasを描画先とする
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //GPImageをこのCanvasに描画
  //TCanvasのStretchDrawに相当する
  LGPGraphic.DrawImage(
              LGPImage,
              MakeRect(0, 0, ClientWidth, ClientHeight),
              0,
              0,
              LGPImage.GetWidth,
              LGPImage.GetHeight,
              UnitPixel,
              LGPImageAttr);

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

end.




04_色の平行移動


次のように,カラーマトリックスの第 5 行目の値を変えると,「色の平行移動」となります.

このサンプルでは,色ごとに TTrackBar で平行移動値を変えるようにしてみました.上から順番に,赤 (R),緑 (G),青 (B) の設定用となっています.


図11
設計時画面
図12
オリジナル画像
図13
操作例

リスト6
色の平行移動のテストプログラム
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  private
    FImageFilePath : String;
    FRTransValue : Single;
    FGTransValue : Single;
    FBTransValue : Single;
    procedure SetTransValue(const Index: Integer; Value: Single);
    procedure SetImageFilePath(const Value: String);
  protected
    procedure Paint; override;
  published
  public
    constructor Create(AOwner: TComponent); override;
    property ImageFilePath : String read FImageFilePath write SetImageFilePath;
    property RTransValue   : Single index 0 read FRTransValue   write SetTransValue;
    property GTransValue   : Single index 1 read FGTransValue   write SetTransValue;
    property BTransValue   : Single index 2 read FBTransValue   write SetTransValue;
  end;

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

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//  各TTrackBarの以下のプロパティは,オブジェクトインスペクタで設定
//  ShowSetRange := False;
//  ThumbLenght  := 20;
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := 'GDI+  カラーマトリックス 色の平行移動';

  //赤色要素の平行移動量用
  TrackBar1.ShowSelRange := False;
  TrackBar1.Height   := 23;
  TrackBar1.Min      := 0;
  TrackBar1.Max      := 100;
  TrackBar1.Position := 0;

  //緑色要素の平行移動量用
  TrackBar2.ShowSelRange := False;
  TrackBar2.Height   := 23;
  TrackBar2.Min      := 0;
  TrackBar2.Max      := 100;
  TrackBar2.Position := 0;

  //青色要素の平行移動量用
  TrackBar3.ShowSelRange := False;
  TrackBar3.Height   := 23;
  TrackBar3.Min      := 0;
  TrackBar3.Max      := 100;
  TrackBar3.Position := 0;

  //TMyPanelを生成して配置
  MyPanel := TMyPanel.Create(Self);
  with MyPanel do begin
    Parent      := Self;
    Align       := alClient;
    Color       := clWhite;
    ParentBackground := False;
    DoubleBuffered   := True;
  end;
  //表示する画像ファイル名
  MyPanel.ImageFilePath := 'dogs018.jpg';

  TrackBar1Change(nil);
end;

//=============================================================================
//  TrackBarのOnChangeイベント処理
//  このイベントは,3つのTTrackBarで共有
//  各色の平行移動量を設定したらTMyPanelクラスを再描画
//  各色の要素の値の範囲は 0 - 1.00 (つまりパーセント) 
//=============================================================================
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;

  MyPanel.RTransValue :=  TrackBar1.Position / 100.0;
  MyPanel.GTransValue :=  TrackBar2.Position / 100.0;
  MyPanel.BTransValue :=  TrackBar3.Position / 100.0;

  MyPanel.Invalidate;
end;

{ TMyPanel }

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

  FRTransValue := 1.00;
  FGTransValue := 1.00;
  FBTransValue := 1.00;
end;

//-----------------------------------------------------------------------------
//  ImageFilePathプロパティの設定用メソッド
//  表示する画像を変更したらTMyPanelを再描画
//-----------------------------------------------------------------------------
procedure TMyPanel.SetImageFilePath(const Value: String);
begin
  FImageFilePath := Value;
  Invalidate;
end;

//-----------------------------------------------------------------------------
//  RTransValue
//  GTransValue
//  BTransValue
//  のプロパティ設定用メソッド
//  プロパティが変更されたら再描画
//-----------------------------------------------------------------------------
procedure TMyPanel.SetTransValue(const Index: Integer; Value: Single);
begin
  case Index of
    0 : FRTransValue := Value;
    1 : FGTransValue := Value;
    2 : FBTransValue := Value;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LColorMatrix : TColorMatrix;
  LGPGraphic   : TGPGraphics;
  LGPImage     : TGPImage;
  LGPImageAttr : TGPImageAttributes;
  i            : Integer;
begin
  inherited;

  //カラーマトリックスの初期化(単位行列を作成)
  FillChar(LColorMatrix,SizeOf(TColorMatrix), #0);
  for i := 0 to 4 do begin
    LColorMatrix[i,i] := 1.0;
  end;

  //各要素の値を代入
  LColorMatrix[4, 0] := FRTransValue;
  LColorMatrix[4, 1] := FGTransValue;
  LColorMatrix[4, 2] := FBTransValue;

  //画像ファイルを指定してTGPImageのオブジェクトを生成
  LGPImage := TGPImage.Create(FImageFilePath);

  //TGPImageAttributesのオブジェクトを生成してカラーマトリックスをセット
  LGPImageAttr := TGPImageAttributes.Create;
  LGPImageAttr.SetColorMatrix(LColorMatrix);

  //TGPGraphicsのオブジェクトを生成.Canvasを描画先とする
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //GPImageをこのCanvasに描画
  //TCanvasのStretchDrawに相当する
  LGPGraphic.DrawImage(
              LGPImage,
              MakeRect(0, 0, ClientWidth, ClientHeight),
              0,
              0,
              LGPImage.GetWidth,
              LGPImage.GetHeight,
              UnitPixel,
              LGPImageAttr);

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

end.




05_輝度調整


前のサンプルでは,第 3 列目までに値をセットしていましたが,次のように,第 4 列目も設定すると輝度調整となります.そのテストプログラムです.
     


図14
設計時画面
図15
オリジナル画像
図16
正の輝度の値
図17
輝度の値が負の場合

リスト7
画像の輝度を調整
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  private
    FImageFilePath : String;
    FBrightness    : Single;
    procedure SetBrightness(const Value: Single);
    procedure SetImageFilePath(const Value: String);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    property ImageFilePath : String read FImageFilePath write SetImageFilePath;
    property Brightness    : Single read FBrightness    write SetBrightness;
  end;

  TForm1 = class(TForm)
    TrackBar1: TTrackBar;
    Label1: TLabel;
    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
  Self.Caption := 'GDI+  カラーマトリックス 輝度調整';

  //輝度の値調整用
  TrackBar1.ShowSelRange := False;
  TrackBar1.Height   := 23;
  TrackBar1.Min      := -50;
  TrackBar1.Max      := 50;
  TrackBar1.Position := 0;

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

    ParentBackground := False;
    DoubleBuffered   := True;

    //表示する画像ファイル名
    ImageFilePath := 'dogs018.jpg';
  end;
  TrackBar1Change(nil);
end;

//=============================================================================
//  TrackBarのOnChangeイベント処理
//  各色の平行移動量を設定したらTMyPanelクラスを再描画
//=============================================================================
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;

  MyPanel.Brightness := TrackBar1.Position / 100.0;
  Label1.Caption     := FormatFloat('##0.00', MyPanel.Brightness);
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタのCreate処理
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FBrightness := 0.00
end;

//-----------------------------------------------------------------------------
//  ImageFilePathプロパティの設定用メソッド
//  表示する画像を変更したらTMyPanelを再描画
//-----------------------------------------------------------------------------
procedure TMyPanel.SetImageFilePath(const Value: String);
begin
  FImageFilePath := Value;
  Invalidate;
end;

//-----------------------------------------------------------------------------
//  Brightnessのプロパティ設定用メソッド
//  プロパティが変更されたら再描画
//-----------------------------------------------------------------------------
procedure TMyPanel.SetBrightness(const Value: Single);
begin
  if FBrightness <> Value then begin
    FBrightness := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LColorMatrix : TColorMatrix;
  LGPGraphic   : TGPGraphics;
  LGPImage     : TGPImage;
  LGPImageAttr : TGPImageAttributes;
  i            : Integer;
begin
  inherited;

  //カラーマトリックスの初期化(単位行列を作成)
  FillChar(LColorMatrix,SizeOf(TColorMatrix), #0);
  for i := 0 to 4 do begin
    LColorMatrix[i,i] := 1.0;
  end;

  //各要素の値を代入
  for i := 0 to 3 do begin
    LColorMatrix[4, i] := FBrightness;
  end;

  //画像ファイルを指定してTGPImageのオブジェクトを生成
  LGPImage := TGPImage.Create(FImageFilePath);

  //TGPImageAttributesのオブジェクトを生成してカラーマトリックスをセット
  LGPImageAttr := TGPImageAttributes.Create;
  LGPImageAttr.SetColorMatrix(LColorMatrix);

  //TGPGraphicsのオブジェクトを生成.Canvasを描画先とする
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //GPImageをこのCanvasに描画
  //TCanvasのStretchDrawに相当する
  LGPGraphic.DrawImage(
              LGPImage,
              MakeRect(0, 0, ClientWidth, ClientHeight),
              0,
              0,
              LGPImage.GetWidth,
              LGPImage.GetHeight,
              UnitPixel,
              LGPImageAttr);

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

end.




06_コントラスト


画像のコントラストの設定は少し面倒です.次のように,各色の輝度と同時に,平行移動量も設定しますが,計算処理が必要です.サンプルのコードで確認してください.


図18
設計時画面
図19
オリジナル画像
図20
正のコントラスト
図21
負のコントラスト

リスト8
画像のコントラストの調整
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  private
    FImageFilePath : String;
    FContrast      : Single;
    procedure SetContrastValue(const Value: Single);
    procedure SetImageFilePath(const Value: String);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    property ImageFilePath : String read FImageFilePath write SetImageFilePath;
    property Contrast      : Single read FContrast      write SetContrastValue;
  end;

  TForm1 = class(TForm)
    TrackBar1: TTrackBar;
    Label1: TLabel;
    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
  Self.Caption := 'GDI+  コントラスト';

  //コントラスト調整用
  TrackBar1.ShowSelRange := False;
  TrackBar1.Height   := 23;
  TrackBar1.Min      := -100;
  TrackBar1.Max      := 100;
  TrackBar1.Position := 0;

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

    ParentBackground := False;
    DoubleBuffered := True;

    //表示する画像ファイル名
    ImageFilePath := 'dogs018.jpg';
  end;
  TrackBar1Change(nil);
end;

//=============================================================================
//  TrackBarのOnChangeイベント処理
//  変更したらTMyPanelクラスを再描画
//=============================================================================
procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  if not Assigned(MyPanel) then exit;

  MyPanel.Contrast := TrackBar1.Position;
  Label1.Caption := IntToStr(TrackBar1.Position);
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタのCreate処理
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FContrast := 0;
end;

//-----------------------------------------------------------------------------
//  ImageFilePathプロパティの設定用メソッド
//  表示する画像を変更したらTMyPanelを再描画
//-----------------------------------------------------------------------------
procedure TMyPanel.SetImageFilePath(const Value: String);
begin
  FImageFilePath := Value;
  Invalidate;
end;

//-----------------------------------------------------------------------------
//  Contrastのプロパティ設定用メソッド
//  プロパティが変更されたら再描画
//-----------------------------------------------------------------------------
procedure TMyPanel.SetContrastValue(const Value: Single);
begin
  if FContrast <> Value then begin
    FContrast := Value;
    Invalidate;
  end;
end;

//-----------------------------------------------------------------------------
//  TMyPanelクラスのPaintメソッド
//  実際の描画コード
//-----------------------------------------------------------------------------
procedure TMyPanel.Paint;
var
  LColorMatrix : TColorMatrix;
  LGPGraphic   : TGPGraphics;
  LGPImageAttr : TGPImageAttributes;
  LGPBitmap    : TGPBitmap;
  LScale       : Single;
  LAppend      : Single;
  i            : Integer;
begin
  inherited;

  //画像ファイルを指定してTGPBitmapのオブジェクトを生成
  //これまでのサンプルと変えて,TGPBitmapにしてみた
  LGPBitmap := TGPBitmap.Create(FImageFilePath);

  //カラーマトリックスの初期化(単位行列を作成)
  FillChar(LColorMatrix,SizeOf(TColorMatrix), #0);
  for i := 0 to 4 do begin
    LColorMatrix[i,i] := 1.0;
  end;

  //各要素の値を代入
  //計算式はMSDNの記事による
  LScale := (100.0 + FContrast) / 100.0;
  for i := 0 to 2 do begin
    LColorMatrix[i, i] := LScale;
  end;
  LAppend := 0.5 * (1.0 - LScale);
  for i := 0 to 2 do begin
    LColorMatrix[4, i] := LAppend;
  end;

  //TGPImageAttributesのオブジェクトを生成してカラーマトリックスをセット
  LGPImageAttr := TGPImageAttributes.Create;
  LGPImageAttr.SetColorMatrix(LColorMatrix);

  //TGPGraphicsのオブジェクトを生成.Canvasを描画先とする
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //GPBitmapをこのCanvasに描画
  //TCanvasのStretchDrawに相当する
  LGPGraphic.DrawImage(
                LGPBitmap,
                MakeRect(0, 0, ClientWidth, ClientHeight),
                0,
                0,
                LGPBitmap.GetWidth,
                LGPBitmap.GetHeight,
                UnitPixel,
                LGPImageAttr);

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

end.




07_ガンマ値の設定 ( ガンマ補正 )


カラーマトリックスを使用するサンプルではありませんが,輝度,コントラストの調整のサンプルを掲載しましたので,ガンマ補正のサンプルも作成しました.
作成したと言っても,カラーマトリックスの設定で使用している TGPImageAttributes の関数に,ガンマ値を設定する SetGamma という関数があります.これの使用例です.

ガンマ補正の機能は,おそらく写真画像の調整用に,よく利用されているかも知れません.
視覚的な意味で,このガンマ補正を「コントラストの調整」と言う場合があります.「コントラストが低いから,(ガンマ曲線) のこの部分を変えてみよう」という言い方をすることがあります.



図22
設計時画面
図23
オリジナル画像
図24
ガンマ値を大きくした場合
図25
ガンマ値が 1 以下の場合

リスト9
ガンマ値の設定テスト
unit Unit1;

interface

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

type
  TMyPanel = class(TPanel)
  private
    FImageFilePath : String;
    FGammaValue    : Single;
    procedure SetGrammaValue(const Value: Single);
    procedure SetImageFilePath(const Value: String);
  protected
    procedure Paint; override;
  public
    constructor Create(AOwner: TComponent); override;
    property ImageFilePath : String read FImageFilePath write SetImageFilePath;
    property GammaValue    : Single read FGammaValue    write SetGrammaValue;
  end;

  TForm1 = class(TForm)
    TrackBar1: TTrackBar;
    Label1: TLabel;
    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
  Self.Caption := 'GDI+  ガンマ値設定 ( ガンマ補正 )';

  //ガンマ値設定用
  //ガンマ値の設定は,オリジナルが1.0なので少し工夫してみた
  TrackBar1.ShowSelRange := False;
  TrackBar1.Height   := 23;
  TrackBar1.Min      := 0;
  TrackBar1.Max      := 400;
  TrackBar1.Position :=  (TrackBar1.Max - TrackBar1.Min) div 2;;

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

    ParentBackground := False;
    DoubleBuffered := True;

    //表示する画像ファイル名
    ImageFilePath := 'dogs018.jpg';
  end;
  TrackBar1Change(nil);
end;

//=============================================================================
//  TrackBarのOnChangeイベント処理
//  変更したらTMyPanelクラスを再描画
//  ガンマ値の設定は,オリジナルが1.0なので少し工夫してみた
//  TTrackBarの中央と1として,1未満と1以上の場合の処理を分けた
//=============================================================================
procedure TForm1.TrackBar1Change(Sender: TObject);
var
  HalfV : Integer;
  V     : Single;
begin
  if not Assigned(MyPanel) then exit;

  HalfV := (TrackBar1.Max - TrackBar1.Min) div 2;
  if TrackBar1.Position < HalfV then begin
    V := 1.00 - (HalfV - TrackBar1.Position) / (HalfV - TrackBar1.Min) * 0.8;
  end else begin
    V := (TrackBar1.Position - HalfV) / (TrackBar1.Max - HalfV) * 1.50 + 1.00;
  end;
  MyPanel.GammaValue := V;
  Label1.Caption     := FormatFloat('##0.00', MyPanel.GammaValue);
end;

{ TMyPanel }

//-----------------------------------------------------------------------------
//  コンストラクタのCreate処理
//-----------------------------------------------------------------------------
constructor TMyPanel.Create(AOwner: TComponent);
begin
  inherited;
  FGammaValue := 1.0;
end;

//-----------------------------------------------------------------------------
//  ImageFilePathプロパティの設定用メソッド
//  表示する画像を変更したらTMyPanelを再描画
//-----------------------------------------------------------------------------
procedure TMyPanel.SetImageFilePath(const Value: String);
begin
  FImageFilePath := Value;
  Invalidate;
end;

//-----------------------------------------------------------------------------
//  GrammaValueのプロパティ設定用メソッド
//  プロパティが変更されたら再描画
//-----------------------------------------------------------------------------
procedure TMyPanel.SetGrammaValue(const Value: Single);
begin
  if FGammaValue <> Value then begin
    FGammaValue := Value;
    Invalidate;
  end;
end;

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

  //画像ファイルを指定してTGPImageのオブジェクトを生成
  LGPImage := TGPImage.Create(FImageFilePath);

  //TGPImageAttributesのオブジェクトを生成してガンマ値をセット
  LGPImageAttr := TGPImageAttributes.Create;
  LGPImageAttr.SetGamma(FGammaValue);

  //TGPGraphicsのオブジェクトを生成.Canvasを描画先とする
  LGPGraphic := TGPGraphics.Create(Canvas.Handle);

  //GPImageをこのCanvasに描画
  //TCanvasのStretchDrawに相当する
  LGPGraphic.DrawImage(
              LGPImage,
              MakeRect(0, 0, ClientWidth, ClientHeight),
              0,
              0,
              LGPImage.GetWidth,
              LGPImage.GetHeight,
              UnitPixel,
              LGPImageAttr);

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

end.




08_モノクロのマスク画像


透過色を指定して,TBitmap の Mask メソッドを実行すると,下図のようなビットマップ画像のマスク画像が作成されます.GDI+ で,このモノクロのマスク画像を描画するサンプルです.ただし,このサンプルコードは,アルファチャンネルを処理してくれません.

マスク画像の作成は,画像をアイコン形式の画像に変換する時,あるいはマウスカーソルの画像を作成する時に必要となる処理です.



図26
TBitamp.Mask メソッドによるモノクロのマスク画像

リスト10
TBitamp.Mask メソッドによるモノクロのマスク画像
アルファチャンネルの処理はしない
procedure TForm1.FormPaint(Sender: TObject);
var
  LSrcBitmap : TBitmap;
  LDstBitmap : TBitmap;
begin
  //元の画像と処理後の画像用のビットマップを生成
  LSrcBitmap  := TBitmap.Create;
  LDstBitmap := TBitmap.Create;

  try
    //画像を読み込む
    LSrcBitmap.LoadFromFile('503.bmp');
    LDstBitmap.Assign(LSrcBitmap);

    //透過色を指定してマスク画像を作成
    LDstBitmap.TransparentColor := LDstBitmap.Canvas.Pixels[0, LDstBitmap.Height - 1];
    LDstBitmap.Mask(LDstBitmap.TransparentColor);

    //元の画像と処理後の画像を表示
    Canvas.Draw(10, 10, LSrcBitmap);
    Canvas.Draw(10, LSrcBitmap.Height + 20, LDstBitmap);
  finally
    FreeAndNil(LDstBitmap);
    FreeAndNil(LSrcBitmap);
  end;
end;


次のマトリックスを使用すると,画像のモノクロマスク画像を描画できます.
このサンプルの場合,TGPBitmap がモノクロのマスク画像になるのではなく,TGPImageAttributes の設定内容で Canvas に描画することになります.


図27
設計時画面
図28
ColorMatrix によるモノクロのマスク画像

リスト11
ColorMatrix によるモノクロのマスク画像を描画
unit Unit1;

interface

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

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

var
  Form1: TForm1;

implementation

{$R *.dfm}

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

//=============================================================================
//  OnPaintイベント
//  実際の描画コード
//=============================================================================
procedure TForm1.FormPaint(Sender: TObject);
const
  ColorMatrix : TColorMatrix = (
    (  0.0,   0.0,   0.0,   0.0,  0.0),
    (  0.0,   0.0,   0.0,   0.0,  0.0),
    (  0.0,   0.0,   0.0,   0.0,  0.0),
    ( -1.0,  -1.0,  -1.0,   1.0,  0.0),
    (  1.0,   1.0,   1.0,   0.0,  1.0));

var
  LObjWidth         : Integer;
  LObjHeight        : Integer;
  LColor            : TGPColor;
  LGDPSrcBitmap     : TGPBitmap;
  LGDPImgAttributes : TGPImageAttributes;
  LGDPGraphic       : TGPGraphics;
begin
  //画像ファイルをTGPBitmapに読み込み,幅と高さを取得
  LGDPSrcBitmap := TGPBitmap.Create('503.bmp');
  LObjWidth     := LGDPSrcBitmap.GetWidth;
  LObjHeight    := LGDPSrcBitmap.GetHeight;

  //透過色を指定して,マトリックスの値を設定
  LGDPImgAttributes := TGPImageAttributes.Create;
  LGDPSrcBitmap.GetPixel(0, LObjHeight - 1, LColor);
  LGDPImgAttributes.SetColorKey(LColor, LColor);
  LGDPImgAttributes.SetColorMatrix(ColorMatrix,
                                   ColorMatrixFlagsDefault,
                                   ColorAdjustTypeDefault);

  //描画先はこのCanvasとし,処理前の画像を表示
  LGDPGraphic := TGPGraphics.Create(Canvas.Handle);
  LGDPGraphic.DrawImage(LGDPSrcBitmap, 10, 10, LObjWidth, LObjHeight);

  //TGPImageAttributesで処理した画像を表示
  LGDPGraphic.DrawImage(LGDPSrcBitmap,
                       MakeRect(10, LObjHeight + 20, LObjWidth, LObjHeight),
                       0, 0, LObjWidth, LObjHeight,
                       UnitPixel,
                       LGDPImgAttributes);

  LGDPSrcBitmap.Free;
  LGDPImgAttributes.Free;
  LGDPGraphic.Free;
end;

end.




09_カラーマトリックスによるアイコン画像の色操作の例


ここまでのサンプルでは,JPEG 形式の写真画像で色操作のテストをしてきました.
本サンプルは PNG 形式のアイコン画像でのテストです.アイコンの画像の色合いを変更したい場合にも利用可能です.上のサンプルと違い,カラーマトリックスの各要素を個別に操作できるようにしてみましたが,半透明度と色の平行移動の設定はありません.

以下に実際の操作例をいくつか掲載しておきます.
以下の記事のサンプルにこのような機能を実装すれば便利かも知れません.
このような機能は多くの画像加工や画像編集のアプリケーションに実装されていますが,その実装内容や操作方法は様々です.

本サンプルでは画像をリサイズして拡大表示していますが,アイコン画像の場合はリサイズはせずに単なる拡大表示とするのが一般的です.


[備考]
本サンプルだけではなく,TGPImageAttributes で属性を指定した描画では,元の TGPBitmap の内容は変化しません.したがって,設定内容は元の TGPBitmap あるいは TGPImage に対するものとなります.表示されているのは,描画先の TCanvas への描画結果です.


図29
設計時の画面
図30
元の画像
図31
ブルー系統の色に変更した例
図32
ゴールド系の色にしてみた例

下図は,以下のサイトからダウンロードした PNG 形式のアイコン画像での操作例です.



図33
元の画像
図34
操作例

下図は,当サイトのサンプルによく登場するヒヨコの例です.
変更した画像を保存して,再度読み込んで操作すると,また別の色合いに変更できます.


図35
元の画像
図36
体が赤っぽいヒヨコに変化
図37
ピンク系統の色に変更
図38
体が緑っぽいヒヨコに変更
図39
体が青っぽいヒヨコに変更

本サンプルには色の平行移動のコードはありません.下図は上の色の平行移動のサンプルコードを実行した結果です.色の平行移動だけでも画像の色合いが変わることが分かります.


図40
元の画像
図41
赤だけを100 % 平行移動した結果
図42
青だけを 100 % 平行移動した結果

リスト12
カラーマトリックスによるアイコン画像の色操作の例
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Image1: TImage;
    OpenPictureDialog1: TOpenPictureDialog;
    GroupBox1: TGroupBox;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    SpinEdit_RedInRed: TSpinEdit;
    SpinEdit_GreenInRed: TSpinEdit;
    SpinEdit_BlueInRed: TSpinEdit;
    GroupBox2: TGroupBox;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    SpinEdit_RedInGreen: TSpinEdit;
    SpinEdit_GreenInGreen: TSpinEdit;
    SpinEdit_BlueInGreen: TSpinEdit;
    GroupBox3: TGroupBox;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    SpinEdit_RedInBlue: TSpinEdit;
    SpinEdit_GreenInBlue: TSpinEdit;
    SpinEdit_BlueInBlue: TSpinEdit;
    Edit1: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure SpinEdit_RedInRedChange(Sender: TObject);
    procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private
    { Private 宣言 }
    FLoadInitDir   : string;
    FImageFilePath : string;
    FMemStream     : TBytesStream;
    FGPBitmap      : TGPBitmap;
    FGPImageAttr   : TGPImageAttributes;
    FImgDispWidth  : Integer;
    FImgDispHeight : Integer;
    procedure DrawGPBitmaToTImage;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  //ファイルの読み出しの初期ディレクトリ
  FLoadInitDir := ExpandFileName('.\Images');
end;

//=============================================================================
//  フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  if Assigned(FGPBitmap) then FreeAndNil(FGPBitmap);
  if Assigned(FMemStream) then FreeAndNil(FMemStream);
  if Assigned(FGPImageAttr) then FreeAndNil(FGPImageAttr);
end;

//=============================================================================
//  [Load Image]
//  画像ファイルの読み込み
//  PNG形式の画像のみ
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LGPGraphic : TGPGraphics;
  LGPBitmap  : TGPBitmap;
begin
  OpenPictureDialog1.Filter     := '画像ファイル|*.png';
  OpenPictureDialog1.InitialDir := FLoadInitDir;
  if not OpenPictureDialog1.Execute then exit;

  FImageFilePath := OpenPictureDialog1.FileName;
  FLoadInitDir   :=  ExtractFileDir(FImageFilePath);


  if Assigned(FGPBitmap)  then FreeAndNil(FGPBitmap);
  if Assigned(FMemStream) then FreeAndNil(FMemStream);
  if Assigned(FGPImageAttr) then FreeAndNil(FGPImageAttr);

  //メモリストリームに画像ファイルを読み込む
  FMemStream := TBytesStream.Create;
  FMemStream.LoadFromFile(FImageFilePath);

  if ExtractFileExt(FImageFilePath) = '.png' then begin
    LGPBitmap := TGPBitmap.Create(TStreamAdapter.Create(FMemStream, soReference));
  end else begin
    exit;
  end;

  //表示サイズの設定
  //横幅256に固定
  FImgDispWidth  := 256;
  FImgDispHeight := Trunc(FImgDispWidth * LGPBitmap.GetHeight / LGPBitmap.GetWidth);

  //マウスクリックで色の値を取得したいので表示サイズのTGPBitmapに変換
  FGPBitmap := TGPBitmap.Create(FImgDispWidth, FImgDispHeight);
  LGPGraphic := TGPGraphics.Create(FGPBitmap);
  LGPGraphic.DrawImage(LGPBitmap,
                       MakeRect(0, 0, FImgDispWidth, FImgDispHeight),
                       0,
                       0,
                       LGPBitmap.GetWidth,
                       LGPBitmap.GetHeight,
                       UnitPixel);

  //色調整用の値を設定するTSpinEditの値
  SpinEdit_RedInRed.Value   := 100;
  SpinEdit_GreenInRed.Value := 0;
  SpinEdit_BlueInRed.Value  := 0;

  SpinEdit_RedInGreen.Value   := 0;
  SpinEdit_GreenInGreen.Value := 100;
  SpinEdit_BlueInGreen.Value  := 0;

  SpinEdit_RedInBlue.Value   := 0;
  SpinEdit_GreenInBlue.Value := 0;
  SpinEdit_BlueInBlue.Value  := 100;

  Edit1.Text := '';

  //描画
  DrawGPBitmaToTImage;

  LGPBitmap.Free;
  LGPGraphic.Free;
end;

//=============================================================================
//  [Reset]
//  読み込んで作成した時の画像に戻す
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  if Assigned(FGPImageAttr) then FreeAndNil(FGPImageAttr);

  SpinEdit_RedInRed.Value   := 100;
  SpinEdit_GreenInRed.Value := 0;
  SpinEdit_BlueInRed.Value  := 0;

  SpinEdit_RedInGreen.Value   := 0;
  SpinEdit_GreenInGreen.Value := 100;
  SpinEdit_BlueInGreen.Value  := 0;

  SpinEdit_RedInBlue.Value   := 0;
  SpinEdit_GreenInBlue.Value := 0;
  SpinEdit_BlueInBlue.Value  := 100;
end;

//=============================================================================
//  各TSpinEditのOnChangeイベント処理
//=============================================================================
procedure TForm1.SpinEdit_RedInRedChange(Sender: TObject);
var
  LColorMatrix : TColorMatrix;
  i            : Integer;
begin
  if not Assigned(FGPBitmap) then exit;

  if TSpinEdit(Sender).Value <= 0 then TSpinEdit(Sender).Value := 0;
  if TSpinEdit(Sender).Value >= 100 then TSpinEdit(Sender).Value := 100;

  //カラーマトリックスの初期化(単位行列を作成)
  FillChar(LColorMatrix, SizeOf(LColorMatrix), #0);
  for i := 0 to 4 do begin
    LColorMatrix[i,i] := 1.0;
  end;

  //
  LColorMatrix[0, 0] := SpinEdit_RedInRed.Value / 100;
  LColorMatrix[1, 0] := SpinEdit_GreenInRed.Value / 100;
  LColorMatrix[2, 0] := SpinEdit_BlueInRed.Value / 100;

  //
  LColorMatrix[0, 1] := SpinEdit_RedInGreen.Value / 100;
  LColorMatrix[1, 1] := SpinEdit_GreenInGreen.Value / 100;
  LColorMatrix[2, 1] := SpinEdit_BlueInGreen.Value / 100;

  //
  LColorMatrix[0, 2] := SpinEdit_RedInBlue.Value / 100;
  LColorMatrix[1, 2] := SpinEdit_GreenInBlue.Value / 100;
  LColorMatrix[2, 2] := SpinEdit_BlueInBlue.Value / 100;

  //TGPImageAttributesが生成されていない時だけ生成する
  //既に生成されている場合は他のTGPImageAttributes設定があるかも知れないので
  if not Assigned(FGPImageAttr) then begin
    FGPImageAttr := TGPImageAttributes.Create;
  end;
  //カラーマトリックスの値をTGPImageAttributesに設定
  FGPImageAttr.SetColorMatrix(LColorMatrix);

  //描画
  DrawGPBitmaToTImage;
end;

//=============================================================================
//  Image1のOnMouseDownイベント処理
//  クリックした位置のピクセルの色の値をTEditに表示
//=============================================================================
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  LGPColor : TGPColor;
begin
  if not Assigned(FGPBitmap) then exit;
  if Button <> mbLeft then exit;

  FGPBitmap.GetPixel(X, Y, LGPColor);
  Edit1.Text := '$' + IntToHex(LGPColor, 8);
end;

//-----------------------------------------------------------------------------
//  TImageへの描画ルーチン
//-----------------------------------------------------------------------------
procedure TForm1.DrawGPBitmaToTImage;
var
  LGPGraphic : TGPGraphics;
begin
  Image1.Picture.Assign(nil);

  Image1.Width  := FImgDispWidth;
  Image1.Height := FImgDispHeight;

  LGPGraphic := TGPGraphics.Create(Image1.Canvas.Handle);
  LGPGraphic.DrawImage(FGPBitmap,
                       MakeRect(0, 0, FImgDispWidth, FImgDispHeight),
                       0,
                       0,
                       FGPBitmap.GetWidth,
                       FGPBitmap.GetHeight,
                       UnitPixel,
                       FGPImageAttr);

  LGPGraphic.Free;
end;

end.




10_アイコン画像におけるガンマ値の効果


本ページの最初の方のサンプルでは主に JPEG 形式の写真画像をテストに使用しています.
前項ではいわゆるアイコンと呼ばれる画像を扱いました.操作していることは同じですが,写真とはまた違う趣があります.ここでガンマ値の効果をアイコン画像で確認してみます.

ガンマ値は一種の明るさの調整ですが,ガンマ値を大きくすると輝度が低い画素は輝度の変化が少なくなりますから,結果的にはコントラスも変化します.
コントラストは,画素間の輝度の相対的な比のことです.輝度 (明るさ),コントラストと共に,このガンマ値の調整も同時に設定する機能を実装すると便利かも知れません.



図43
テスト用の PNG 形式の画像を読み込んで表示
図44
ガンマ値を 2.61 に変更した結果
  • 左上はもとの画像
  • 左の下はガンマ値を変更した画像
  • 右はそれをリサイズして拡大表示した画像

リスト13
アイコン画像におけるガンマ値の効果のテスト
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Image1: TImage;
    OpenPictureDialog1: TOpenPictureDialog;
    GroupBox1: TGroupBox;
    Label1: TLabel;
    SpinEdit_RedInRed: TSpinEdit;
    Image2: TImage;
    Image3: TImage;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure SpinEdit_RedInRedChange(Sender: TObject);
  private
    { Private 宣言 }
    FLoadInitDir   : string;
    FImageFilePath : string;
    FMemStream     : TBytesStream;
    FGPBitmap      : TGPBitmap;
    FGPImageAttr   : TGPImageAttributes;
    FImgDispWidth  : Integer;
    FImgDispHeight : Integer;
    procedure DrawGPBitmaToTImage;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  //ファイルの読み出しの初期ディレクトリ
  FLoadInitDir := ExpandFileName('.\Images');
end;

//=============================================================================
//  フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
  if Assigned(FGPBitmap) then FreeAndNil(FGPBitmap);
  if Assigned(FMemStream) then FreeAndNil(FMemStream);
  if Assigned(FGPImageAttr) then FreeAndNil(FGPImageAttr);
end;

//=============================================================================
//  [Load Image]
//  画像ファイルの読み込み
//  PNG形式の画像のみ
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
  OpenPictureDialog1.Filter     := '画像ファイル|*.png';
  OpenPictureDialog1.InitialDir := FLoadInitDir;
  if not OpenPictureDialog1.Execute then exit;

  FImageFilePath := OpenPictureDialog1.FileName;
  FLoadInitDir   :=  ExtractFileDir(FImageFilePath);


  if Assigned(FGPBitmap)  then FreeAndNil(FGPBitmap);
  if Assigned(FMemStream) then FreeAndNil(FMemStream);
  if Assigned(FGPImageAttr) then FreeAndNil(FGPImageAttr);

  //メモリストリームに画像ファイルを読み込む
  FMemStream := TBytesStream.Create;
  FMemStream.LoadFromFile(FImageFilePath);

  if ExtractFileExt(FImageFilePath) = '.png' then begin
    FGPBitmap := TGPBitmap.Create(TStreamAdapter.Create(FMemStream, soReference));
  end else begin
    exit;
  end;

  //表示サイズの設定
  //横幅256に固定
  FImgDispWidth  := 128;
  FImgDispHeight := Trunc(FImgDispWidth * FGPBitmap.GetHeight / FGPBitmap.GetWidth);

  //ガンマ値設定用TSpinEditの値
  SpinEdit_RedInRed.Value   := 100;

  //描画
  DrawGPBitmaToTImage;
end;

//=============================================================================
//  [Reset]
//  読み込んで作成した時の画像に戻す
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  if Assigned(FGPImageAttr) then FreeAndNil(FGPImageAttr);

  SpinEdit_RedInRed.Value   := 100;
end;

//=============================================================================
//  TSpinEditのOnChangeイベント処理
//=============================================================================
procedure TForm1.SpinEdit_RedInRedChange(Sender: TObject);
var
  LGammaValue : Single;
begin
  if not Assigned(FGPBitmap) then exit;

  //実際にセットする値はこの値の1/100(0から4.00)
  if TSpinEdit(Sender).Value <= 0 then TSpinEdit(Sender).Value := 0;
  if TSpinEdit(Sender).Value >= 500 then TSpinEdit(Sender).Value := 500;

  //TGPImageAttributesが生成されていない時だけ生成する
  //既に生成されている場合は他のTGPImageAttributes設定があるかも知れないので
  if not Assigned(FGPImageAttr) then begin
    FGPImageAttr := TGPImageAttributes.Create;
  end;
  //ガンマ値をTGPImageAttributesに設定
  LGammaValue := SpinEdit_RedInRed.Value / 100;
  FGPImageAttr.SetGamma(LGammaValue);

  //描画
  DrawGPBitmaToTImage;
end;

//-----------------------------------------------------------------------------
//  TImageへの描画ルーチン
//-----------------------------------------------------------------------------
procedure TForm1.DrawGPBitmaToTImage;
var
  LGPGraphic : TGPGraphics;
  LWidth     : Integer;
  LHeight    : Integer;
begin
  Image1.Picture.Assign(nil);
  Image2.Picture.Assign(nil);
  Image3.Picture.Assign(nil);

  LWidth  := FGPBitmap.GetWidth;
  LHeight := FGPBitmap.GetHeight;

  if LWidth <= 64 then begin
    //元の画像を実サイズ表示
    Image2.Width  := LWidth;
    Image2.Height := LHeight;

    LGPGraphic := TGPGraphics.Create(Image2.Canvas.Handle);
    LGPGraphic.DrawImage(FGPBitmap, 0, 0);
    LGPGraphic.Free;


    //ガンマ値を変更した画像を実サイズ表示
    Image3.Top := Image2.Top + Image2.Height + 10;
    Image3.Width  := LWidth;
    Image3.Height := LHeight;

    LGPGraphic := TGPGraphics.Create(Image3.Canvas.Handle);
    LGPGraphic.DrawImage(FGPBitmap,
                         MakeRect(0, 0, LWidth, LHeight),
                         0,
                         0,
                         LWidth,
                         LHeight,
                         UnitPixel,
                         FGPImageAttr);
    LGPGraphic.Free;
  end;


  //拡大リサイズして表示
  Image1.Width  := FImgDispWidth;
  Image1.Height := FImgDispHeight;

  LGPGraphic := TGPGraphics.Create(Image1.Canvas.Handle);
  LGPGraphic.DrawImage(FGPBitmap,
                       MakeRect(0, 0, FImgDispWidth, FImgDispHeight),
                       0,
                       0,
                       LWidth,
                       LHeight,
                       UnitPixel,
                       FGPImageAttr);

  LGPGraphic.Free;
end;

end.




11_TGPImageAttributes を使用した描画結果の保存


TGPImageAttributes を使用した描画の結果は,指定した Canvas に描画されます.元の TGPBitmap の内容は変化しません.だからこそ設定内容を変化させて描画結果を確認できるわけです.
現在の描画されている状態を画像として保存するには,別の TGPBitmap を用意して,その TGPBitmap に同じ TGPImageAttributes の設定で描画します.
以下はその例です.このコードは,前項のサンプルに保存の機能を追加する例です.

下に示すコードは,保存の際に保存用の TGPBitmap を生成する例です.保存の時は TImage への描画時と描画先を変更する例です.



リスト14
保存の際に保存用のTGPBitmapを作成する例
//=============================================================================
//  描画内容を保存
//  保存の際に保存用のTGPBitmapを作成する例
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LGPbitmap  : TGPBitmap;
  LGPGraphic : TGPGraphics;
  LImgGUID   : TGUID;
begin
  if not Assigned(FGPBitmap) then exit;

  //表示サイズで保存するものとする
  //表示サイズと同じTBPBitmapを作成
  LGPbitmap := TGPBitmap.Create(FImgDispWidth, FImgDispHeight);
  try
    DrawGPBitmaToTImage(LGPbitmap);
    //PNG画像として保存
    if GetEncoderClsid('image/png', LImgGUID) >= 0 then begin
      LGPBitmap.Save('000.png', LImgGUID);
    end;
  finally
    FreeAndNil(LGPbitmap);
  end;
end;

//-----------------------------------------------------------------------------
//  描画ルーチン
//-----------------------------------------------------------------------------
procedure TForm1.DrawGPBitmaToTImage(AGPBitmap: TGPBitmap = nil);
var
  LGPGraphic : TGPGraphics;
begin
  Image1.Picture.Assign(nil);

  Image1.Width  := FImgDispWidth;
  Image1.Height := FImgDispHeight;

  if AGPBitmap <> nil then begin
    LGPGraphic := TGPGraphics. Create(AGPBitmap);
  end else begin
    LGPGraphic := TGPGraphics.Create(Image1.Canvas.Handle);
  end;
  LGPGraphic.DrawImage(FGPBitmap,
                       MakeRect(0, 0, FImgDispWidth, FImgDispHeight),
                       0,
                       0,
                       FGPBitmap.GetWidth,
                       FGPBitmap.GetHeight,
                       UnitPixel,
                       FGPImageAttr);

  LGPGraphic.Free;
end;


次のコードは TGPBitmap 型のグローバルな変数を用意してそれを使用する方法です.
まず,このグローバル変数の TGPBitmap に描画して,それを TImage に描画しています.



リスト15
グローバルな変数として専用のTGPBitmapを使用する例
//=============================================================================
//  描画内容を保存
//  グローバルな変数として専用のTGPBitmapを使用する例
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LImgGUID : TGUID;
begin
  if not Assigned(FGPSaveBitmap) then exit;

  //PNG画像として保存
  if GetEncoderClsid('image/png', LImgGUID) >= 0 then begin
    FGPSaveBitmap.Save('000.png', LImgGUID);
  end;
end;

//-----------------------------------------------------------------------------
//  描画ルーチン
//-----------------------------------------------------------------------------
procedure TForm1.DrawGPBitmaToTImage;
var
  LGPGraphic : TGPGraphics;
begin
  Image1.Picture.Assign(nil);

  Image1.Width  := FImgDispWidth;
  Image1.Height := FImgDispHeight;

  if not Assigned(FGPSaveBitmap) then begin
    //表示サイズと同じTBPBitmapを作成
    FGPSaveBitmap := TGPBitmap.Create(FImgDispWidth, FImgDispHeight);
  end;
  //作成したTGPBitmapに読み込んだ画像を属性を指定して描画する
  LGPGraphic := TGPGraphics. Create(FGPSaveBitmap);
  LGPGraphic.DrawImage(FGPBitmap,
                       MakeRect(0, 0, FImgDispWidth, FImgDispHeight),
                       0,
                       0,
                       FGPBitmap.GetWidth,
                       FGPBitmap.GetHeight,
                       UnitPixel,
                       FGPImageAttr);
  LGPGraphic.Free;

  //描画の結果をTImageに描画する
  LGPGraphic := TGPGraphics.Create(Image1.Canvas.Handle);
  LGPGraphic.DrawImage(FGPSaveBitmap, 0, 0);
  LGPGraphic.Free;
end;