Delphi 2009におけるグラフィック機能の強化点

By: Yusuke Konno

Abstract: Delphi 2009で強化されたグラフィック機能について解説します。

Delphi 2009ではTBitmapの32ビット形式サポート、PNGサポート、TCanvas.Drawメソッドの拡張、TImageListの拡張など、グラフィック機能の強化も行われています。今回は新しいグラフィック機能についてできるだけ網羅的に解説を行います。

    TBitmap

    32ビット形式のサポート

Delphi 2009ではTBitmapが32ビット形式をサポートしました。32ビット形式のビットマップは1ピクセルあたりに4バイトのデータを割り当てており、RGB各色が1バイトずつ使用し、更にアルファチャンネル用に1バイトを使用します。32ビット形式ではアルファチャンネルの情報を設定することで半透明なビットマップを作ることが可能です。

なお、アルファチャンネルの値が取り得る値は0~255で、0だと完全に透明、255だと完全に不透明になります。

    32ビット形式のビットマップを表示する

まず最も簡単な例として、既存の32ビット形式のビットマップファイルを読み込んで、TImageに表示するプログラムを作成してみます。なお、読み込む画像は幅・高さ共に100ピクセルで、TImageの幅・高さも100に設定しておきます。

読み込む画像(幅100ピクセル、高さ100ピクセル)

Hide image
original

画像に設定されたアルファ値(黒いほど透明、白いほど不透明)

Hide image
alpha

Code#001TImageへの読み込み

//Code#001:TImageへの読み込み
begin
  //Draw Image
  Image1.Picture := nil;
  Image1.Picture.LoadFromFile('32bitbitmap.bmp');
  Image1.Picture.Bitmap.AlphaFormat := afDefined;//重要!
end;

コード自体はとても簡単です。ビットマップファイルをLoadFromFileで読み込めば良いだけです。ただし、読み込んだ後にTImage.Picture.BitmapのAlphaFormatプロパティをafDefinedに設定しなければなりません。設定を忘れてしまうと画像のアルファチャンネルの値が無視されて表示されてしまいます。その他の注意点は特にありません。実行結果は以下のようになります。

実行結果

Hide image
graphic_001

画像の持つアルファチャンネルに従って描画されています。

    Canvas.Drawで描画する場合

先ほどの例ではTImageを使いましたが、実用的なグラフィックプログラムであればTPaintBoxなどに描画するのが一般的です。今度はTPaintBoxのCanvasに描画を行ってみます。なお、用いる画像は先ほどの例と全く同じものです。

Code#002Canvas.Drawによる描画

var
  BMP: TBitmap;
begin
  //Draw at Canvas
  BMP := TBitmap.Create;
  try
    BMP.LoadFromFile('32bitbitmap.bmp');
    BMP.AlphaFormat := afDefined;
    PaintBox1.Canvas.Draw(0,0,BMP);
  finally
    BMP.Free;
  end;
end;

こちらも簡単なコードです。TBitmapのインスタンスを生成し、LoadFromFileを使ってファイルを読み込みます。やはりここでもAlphaFormatプロパティの設定が重要になっています。Canvas.Drawを実行する前にAlphaFormatプロパティをafDefinedに設定する必要があります。これを怠ると、やはりアルファチャンネルの値が無視されて描画されてしまいます。このコードの実行結果は以下の通りです。

実行結果

Hide image
graphic_002

ちゃんと画像のアルファチャンネルに従って描画されています。

Point
表示・描画の際には、AlphaFormatプロパティをafDefinedに設定することを忘れないようにしましょう。

    アルファチャンネルの生成

今度は、既存の24ビット形式のビットマップ画像に対してアルファチャンネルを生成してみます。使用する画像は先ほどと同じ物(但しアルファチャンネルは未設定)です。また、描画はPaintBoxのCanvasに対して行います。

Code#003:アルファチャンネルの生成

//Code#003:アルファチャンネルの生成
type
  TRGBQArray = array [0..High(Integer) div 4 - 1] of RGBQUAD;
  PRGBQArray = ^TRGBQArray;
var
  BMP: TBitmap;
  PQ: PRGBQArray;
  i: Integer;
  ii: Integer;
begin
  //Create Alpha
  BMP := TBitmap.Create;
  try
    BMP.LoadFromFile('24bitbitmap.bmp');
    BMP.PixelFormat := pf32bit;

    {1ピクセルずつアルファチャンネルの値を設定}
    for i := 0 to BMP.Height - 1 do
    begin
      PQ := BMP.ScanLine[i];//RGBQuadの配列へのポインタを代入する
      for ii := 0 to BMP.Height - 1 do
      begin
        TRGBQuad(PQ[ii]).rgbReserved := 64;//アルファチャンネルの値を64に設定
      end;
    end;

    BMP.AlphaFormat := afDefined;
    PaintBox1.Canvas.Draw(0,0,BMP);
  finally
    BMP.Free;
  end;
end;

まず、TBitmapのインスタンスを生成し、画像を読み込みます。次に、PixelFormatプロパティをpf32bitに設定し、アルファチャンネルを設定できるようにします。その後は、ScanLineプロパティを使って1ピクセルずつアルファチャンネルの値を設定していきます。描画時の注意点は先ほどのCanvas.Drawの時と同じです。

実行結果は以下の通りになります。

実行結果

Hide image
graphic_003

画像全体に同じアルファチャンネルの値を設定したため、画像全体がちゃんと半透明で表示されていますね。

    PNGのサポート

    新たに追加されたTPNGImage

Delphi 2009からはついにPNGが標準でサポートされました。これでようやくビットマップ、JPEG、GIF、PNGの標準的なライブラリが全て揃ったことになります。

PNGを扱うクラスはTPNGImageです。使用するにはまずuses節にPNGImageを追加します

JPEGPNGGIFのユニット名
困ったことに、JPEG、GIF、PNGのユニット名は命名規則が統一されていません。
JPEG:JPEG.pas(Delphi 3から)
GIF:GIFImg.pas(Delphi 2007から)
PNG:PNGImage.pas(Delphi 2009から)
これは、元々GIFがAnders Melander氏制作のライブラリ、PNGがGustavo Huffenbacher Daud氏制作のライブラリであるからです。
JPEGPNGGIFのヘルプはどこに?
極めて残念なことですが、Delphi 2009 Help Update 1の時点では、JPEG、PNG、GIFのドキュメントは収録されていません。
JPEGについての使用方法は流石に周知されているとは思いますが、PNG、GIFについての情報が付属のヘルプから得られないのは大変不便です。

JPEGのドキュメント
少なくともDelphi 2006のヘルプには収録されているので、
Turbo Delphi Explorerを導入すればTJPEGImageについてのリファレンスを無料で入手できます。
当然ですがDelphi 3~2006をお持ちであればそれらに収録されているドキュメントを参照すればOKです。

PNGのドキュメント
もし、旧バージョン等でTPNGImageを導入していたのであれば、
付属しているヘルプを参照すればかなり参考になりますが、残念なことに現状では新規に入手する方法がありません。

GIFのドキュメント
TGIFImageの作者のページは閉鎖されていますが、ライブラリ自体は配布されているサイトがあります。
http://www.tolderlund.eu/delphi/
ヘルプも同時に配布されているので、それを利用すればなんとかなるでしょう。
(ただし、多少の読み替えが必要な場合があります)

    PNGの作成・読み込み・保存

TPNGImageの使い方は非常に簡単で、TJPEGImageのようなグラフィッククラスを使ったことがあればすぐに使いこなせるように出来ています。

ここではまず、ビットマップをPNGに変換してファイルに保存する例を見てみます。

Code#004BitmapからPNGへの変換

//Code#004:BitmapからPNGへの変換
uses
  ・・・・, PNGImage;
    
{略}

var
  BMP: TBitmap;
  PNG: TPNGImage;
begin
  BMP := TBitmap.Create;
  PNG := TPNGImage.Create;
  try
    BMP.LoadFromFile('hogehoge.bmp');//24bit Bitmap
    PNG.Assign(BMP);
    PNG.CompressionLevel := 5;
    PNG.Filters := [pfNone,pfSub,pfUp,pfAverage,pfPaeth];
    PNG.SaveToFile('HogeHoge.png');
  finally
    BMP.Free;
    PNG.Free
  end;
end;

ビットマップを変換してPNGを生成するのもJPEGと同じ要領で、極めて簡単です。AssignするだけでOKです。ただし、Assignの引数に指定するビットマップが32ビット形式の場合はエラーを起こしてしまいます。32ビット形式から変換をする際には別の変換方法を採用しなければなりません。

PNGの圧縮レベルはCompressionLevelプロパティで指定します。JPEGの際は1~100の整数値を指定し、数値が大きいほど高品質(=ファイルサイズは大きい)でした。PNGの場合は、0~9の整数値を指定し、数値が小さいほど速度重視(=ファイルサイズは大きい)、数値が大きいほど圧縮率重視(=ファイルサイズは小さい)になります。

PNGのフィルターはFiltersプロパティで指定します。Filtersプロパティには複数のフィルターを指定することが可能です。フィルターはpfNone、pfSub、pfUp、pfAverage、pfPaethの5種類を指定できます。特段の理由がなければ上記コードのように全てのフィルターを指定することが推奨されています。

ファイルの読み込みについてはLoadFromFile、ファイルへの保存についてはSaveToFileメソッドが使えます。つまり、TBitmapやTJPEGImageと全く同じ要領でPNGファイルの読み書きが可能です。

    アルファチャンネル

TPNGImageはアルファチャネルもサポートしています。ここでは、既存の24ビットPNG画像に対して新たにアルファチャンネルを設定する例を紹介します。アルファチャネルを設定するには、AlphaScanlineを使うと便利です。

Code#005:アルファチャンネルの作成

//Code#005:アルファチャンネルの作成
var
  i,ii: Integer;
  PNG: TPNGImage;
begin
  PNG := TPngImage.Create;
  try
    PNG.LoadFromFile('alpha.png');//24bit PNG(No Alpha)
    PNG.CreateAlpha;//アルファチャンネルの生成

    {1ピクセルずつアルファチャンネルの値を設定}
    for i := 0 to PNG.Height - 1 do
    begin
      for ii := 0 to PNG.Width - 1 do
      begin
        PNG.AlphaScanline[i]^[ii] := 128;
      end;
    end;
    PNG.SaveToFile('alpha.png');
  finally
    PNG.Free;
  end;
end;

まずTPNGImageのインスタンスを生成し、ファイルを読み込みます。次にCreateAlphaメソッドを実行します。32ビット形式でないPNG画像の場合はCreateAlphaメソッドを実行してからではないとアルファチャンネルの設定が出来ません。CreateAlphaメソッドを実行したら、1ピクセルずつAlphaScanlineを使ってアルファチャンネルの値を設定していきます。

実行結果は以下の通りです。

実行結果

Hide image
graphic_004

画像全体が半透明で表示されています。TBitmapと違い、表示前にAlphaFormatプロパティを設定するといったような作業は不要です。

    32ビット形式のビットマップからの変換

PixelFormat = pf32bitTBitmapから直にAssignするとエラーになるので、若干面倒なコードを書かなければなりません。

Code#00632ビット形式のビットマップからの変換

//Code#006:32ビット形式のビットマップからの変換
type
  TRGBQArray = array [0..High(Integer) div 4 - 1] of RGBQUAD;
  PRGBQArray = ^TRGBQArray;
  TRGBTArray = array [0..High(Integer) div 3 - 1] of RGBTRIPLE;
  PRGBTArray = ^TRGBTArray;
var
  PNG: TPngImage;
  i,ii: Integer;
  BPQ: PRGBQArray;
  CQ: RGBQUAD;
  PPT: PRGBTArray;
  CT: RGBTRIPLE;
  BMP: TBitmap;
begin
  BMP := TBitmap.Create;
  PNG := TPngImage.CreateBlank(COLOR_RGBALPHA,8,100,100);
  try
    BMP.LoadFromFile('original.bmp');
    for i := 0 to BMP.Height - 1 do
    begin
      BPQ := BMP.ScanLine[i];
      PPT := PNG.ScanLine[i];
      for ii := 0 to BMP.Width - 1 do
      begin
        CQ := BPQ[ii];
        CT.rgbtBlue := CQ.rgbBlue;
        CT.rgbtGreen := CQ.rgbGreen;
        CT.rgbtRed := CQ.rgbRed;
        PPT[ii] := CT;
        PNG.AlphaScanline[i]^[ii] := CQ.rgbReserved;
      end;
    end;

    PNG.SaveToFile('result.png');
  finally
    BMP.Free;
    PNG.Free;
  end;
end;

まず、TPNGImageのインスタンスをCreateメソッドではなくCreateBlankメソッドで作成します。なぜなら、TPNGImageWidthHeightReadOnlyなので、新しい値を代入できません。今回のコードでは空のPNGにビットマップの情報を1ピクセルずつ移植するという処理なので、WidthとHeightを設定しないままTPNGImageのインスタンスを生成するCreateは役に立ちません。

CreateBlankメソッドの第1引数にはCOLOR_RGBALPHAを指定します。これで32ビット形式のPNGとなるので、別途CreateAlphaメソッドを実行する必要がなくなります。第2引数は8をセットしておけば1ピクセルあたりの情報量はRGBAの4バイトになります。第3,4引数には新しいイメージの幅と高さ(今回の例では両方ともビットマップ画像の幅と高さである100)をセットします。

変換はビットマップの各ピクセルの情報のうち、RGBをRGBTripleの変数を経由してScanlineで、Alpha値をAlphaScanlineで渡しています。

    TCanvas

    半透明描画が可能なDrawメソッド

TCanvas.Drawメソッドに新しくオーバーロードが追加されました。従来は引数が3つでしたが、新たなオーバーロードは4つめの引数にOpacityというバイト型をとる物が追加されています。これは描画時にOpacityに値を指定することによって、Graphicで指定した画像を半透明で描画することが出来るという機能です。

Code#007TCanvas.Drawのオーバーロード

//Code#007:TCanvas.Drawのオーバーロード
var
  BMP: TBitmap;
begin
  BMP := TBitmap.Create;
  try
    BMP.LoadFromFile('original.bmp');
    PaintBox1.Canvas.Draw(0,0,BMP);
    PaintBox2.Canvas.Draw(0,0,BMP,64);
  finally
    BMP.Free;
  end;
end;

実行結果

Hide image
graphic_tcanvasdrawopacity

左側が従来のメソッド、右側が新しいオーバーロードされたメソッドで描画したものです。
右側はOpacityに64を指定しているため、画像全体が半透明になって描画されています。

    TImageList

    PNGおよび32ビット形式ビットマップのサポート

PNGのサポートと32ビット形式のサポートに伴い、TImageListもこれらの形式に対応しました。PNGの表示はもちろん、アルファチャンネルにも対応しているため、半透明な画像を表示することも可能です。

今回はDelphi 2009に付属しているグラフィック素材集であるGlyFXに収録されている32 * 32のPNGイメージ(アルファチャンネル設定済み)をTImageListに読み込んで、それらをTListViewに表示してみます。

    Step1:Width、Height、ColorDepthの設定

まず、画像を追加する前にTImageListのWidthとHeightをそれぞれ画像の幅と高さに合わせます。今回は両方とも32に設定しておきます。それから、非常に重要なことですが、32ビット形式のビットマップまたはPNGを追加する際には、画像を追加する前にColorDepthプロパティをcd32bitに設定する必要があります。画像の追加後にColorDepthプロパティの設定を変更すると、TImageListに追加されていた画像が全て消えてしまいます。

    Step2:画像の追加

GlyFXのPNGイメージを追加します。今回使用する素材は%Program Files%\Common Files\CodeGear Shared\Images\GlyFX\Icons\PNG\32x32に収録されている、home_brown_32.png、home_green_32.png、home_purple_32.png、home_yellow_32.pngの4画像です。

Hide image
graphic_005

これらの画像には余白部分が黒ですが、アルファチャンネルの値が0(完全に透明)に設定してあります。追加時点で既にそれが反映されていることが分かります。

    Step3:表示

TListViewのLargeIamgeプロパティにImageListを設定し、Itemを4つほど生成します。表示結果は以下のようになります。

Hide image
graphic_006

ちゃんとアルファチャンネルに従って表示されていますね。

Server Response from: ETNASC04