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

提供: Support
移動先: 案内検索

この記事は以前EDNサイトに作成されていた記事を転載したものです。 ヘルプ・ドキュメントの箇所を除き、当時の内容のまま掲載しています。

概要

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ピクセル)
    D2009 01.png
    • 画像に設定されたアルファ値(黒いほど透明、白いほど不透明)
    D2009 02.png
    • Code#001:TImageへの読み込み
    //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に設定しなければなりません。設定を忘れてしまうと画像のアルファチャンネルの値が無視されて表示されてしまいます。その他の注意点は特にありません。実行結果は以下のようになります。
    • 実行結果
    D2009 03.png
    画像の持つアルファチャンネルに従って描画されています。

Canvas.Drawで描画する場合

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

  • Code#002:Canvas.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に設定する必要があります。これを怠ると、やはりアルファチャンネルの値が無視されて描画されてしまいます。このコードの実行結果は以下の通りです。
  • 実行結果
    D2009 04.png
    ちゃんと画像のアルファチャンネルに従って描画されています。
  • ※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の時と同じです。
    実行結果は以下の通りになります。
    D2009 05.png
    画像全体に同じアルファチャンネルの値を設定したため、画像全体がちゃんと半透明で表示されていますね。

PNGのサポート

新たに追加されたTPNGImage

Delphi 2009からはついにPNGが標準でサポートされました。これでようやくビットマップ、JPEG、GIF、PNGの標準的なライブラリが全て揃ったことになります。
PNGを扱うクラスはTPNGImageです。使用するにはまずuses節にPNGImageを追加します

  • JPEG、PNG、GIFのユニット名
    困ったことに、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氏制作のライブラリであるからです。

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

TPNGImageの使い方は非常に簡単で、TJPEGImageのようなグラフィッククラスを使ったことがあればすぐに使いこなせるように出来ています。 ここではまず、ビットマップをPNGに変換してファイルに保存する例を見てみます。

  • Code#004:Bitmapから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を使ってアルファチャンネルの値を設定していきます。
    実行結果は以下の通りです。
    D2009 06.png
    画像全体が半透明で表示されています。TBitmapと違い、表示前にAlphaFormatプロパティを設定するといったような作業は不要です。

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

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

  • Code#006:32ビット形式のビットマップからの変換
    //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メソッドで作成します。なぜなら、TPNGImageのWidthとHeightはReadOnlyなので、新しい値を代入できません。今回のコードでは空の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#007:TCanvas.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;
    
  • 実行結果
    D2009 07.png
    左側が従来のメソッド、右側が新しいオーバーロードされたメソッドで描画したものです。右側はOpacityに64を指定しているため、画像全体が半透明になって描画されています。

TImageList

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

PNGのサポートと32ビット形式のサポートに伴い、TImageListもこれらの形式に対応しました。PNGの表示はもちろん、アルファチャンネルにも対応しているため、半透明な画像を表示することも可能です。
今回はDelphi 2009に付属しているグラフィック素材集であるGlyFXに収録されている32x32の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画像です。
    D2009 08.png
    これらの画像には余白部分が黒ですが、アルファチャンネルの値が0(完全に透明)に設定してあります。追加時点で既にそれが反映されていることが分かります。
  • Step3:表示
    TListViewのLargeIamgeプロパティにImageListを設定し、Itemを4つほど生成します。表示結果は以下のようになります。
    D2009 09.png
    ちゃんとアルファチャンネルに従って表示されていますね。