戻る ホーム 上へ 進む

2.7 TBitmap

TBitmap は頻繁に利用する割には Delphi のドキュメントがよくできていない クラスです。TBitmap はオフスクリーンビットマップとメモリデバイスコンテキストと ビットマップ用のパレットを一つにまとめたクラスで、非常に多彩な機能を もち、かつ、本来複雑なはずの操作を大幅に単純化してくれます。この節では、TBitmap のいろいろな機能を解説します。

2.7.1 ビットマップの基礎

TBitmap の説明に入る前に DDB と DIB に関して簡単に説明しておきます。

現在、Windows が標準でサポートしているビットマップには2種類有ります。Device Dependent Bitmap(DDB) と Device Independent Bitmap Section(DIB Section) です。

DDB は Windows のビデオドライバが直接扱うことのできるビットマップで、その内部構造はビデオドライバやビデオモードによって異なります。ビットマップのピクセルをプログラムから直接さわることはできません。GDI の API を介して操作することになります。

DDB の最大の特徴、というか欠点は パレットをサポートしているビデオモードでは 色情報(色の RGB 値) が DDB 内に無いことです。2.6 で詳しく説明しましたが パレットをサポートしているビデオモードでは DDB は常に パレットと組で使う必要が有ります。この場合、 DDB の色はパレットの FGマッピングという非常にデリケートなものに依存しているため、取り扱いには細心の注意が必要になります。FGマッピングについては 2.6.1.5 フォアグラウンド実体化(FG実体化)とバックグラウンド実体化(BG実体化)を見てください。

パレットをサポートしていないビデオモードでは、DDB のピクセル値は RGB そのものか、RGB 値から下位ビットを端折った値になります。しかし、パレットをサポートしているビデオモードでは DDB のピクセル値はシステムパレットのインデックスです。ただし、このシステムパレットは仮想的なシステムパレットで、このシステムパレットと論理パレットとの関係がパレットのFGマッピングです。DDB の色の RGB値は下図のようにして決まります。

図 2.7-1 DDB と論理パレットの関係

図からわかるように、DDB のピクセル値(システムパレットインデックス)に対応する RGB 値は パレット内のFGマッピングと RGB 値のテーブルによって決まります。FGマッピングは 「フォアグラウンド実体化(FG実体化)とバックグラウンド実体化(BG実体化)」 で説明したように、パレットが最初に実体化されたときにその時のシステムパレットの状態に基づいて作成されるシステムパレットと論理パレットのインデックスの変換表です。

DDB のピクセル値は別の見方をすれば、それと対になっているパレットがスクリーンデバイスに FG実体化したときのシステムパレットのインデックスが入っていると考えることができます。つまり、ビットマップはパレットが FG実体化した仮想的なスクリーンだと考えることもできます。

さて次は DIB (Device Independent Bitmap)を説明します。DIB はメモリ上やファイル中の形式がきちんと定義されたビットマップの形式です。形式は以下のようになっています。

図 2.7-2 DIB の構造

図 2.7-3 DIB の構造

DIB は 16bpp(Bit Per Pixel), 24bpp, 32bpp 形式では各ピクセルが RGB 値を持ちますが、1bpp, 4bpp, 8bpp では 上図のように DIB 内のカラーテーブルのインデックスを持ちます。DDB に比べて RGB値と ピクセル値との関係が非常にシンプルです。TBitmap は DIB のカラーテーブルから論理パレット(Palette プロパティ に格納されたパレット)を作りますので、図で示されていますように、カラーテーブルと色構成と論理パレットの色構成は全く同じです。つまり、TBitmap の中に DIB Section が入っているときは、DIB のピクセル値は論理パレットのインデックスをあらわすことになります。

Windows 3.1 までは DIB はデータに過ぎず、DDB と相互に変換したり、表示するための API は用意されていましたが GDI を使って線を引いたり文字を書いたりすることはできませんでした。Win32 では DIB Section が導入されたため、 DIB を GDI のデバイスコンテキストで DDB とほぼ同様に使えるようになりました。DIB Section は DIB の ビットマップハンドルを提供し、メモリデバイスコンテキストに選択できます。

図 2.7-4 DIB と DIB Section の関係

DIB Section は CreateDIBSection API で作成します。CreateDIBSection は ビットマップヘッダとカラーテーブルから DIB Section を作り、そのビットマップハンドルを返します。重要な点は DIB Section がカラーテーブルを含んでおり、ピクセルデータを含んでいないことです。カラーテーブルが含まれていることにより DIB Section はパレットに依存しません。また、ピクセルデータは相変わらずメモリ上に有ってプロセスから直接アクセス可能なので、GDI を介すよりはるかに高速にピクセルを変更することができます。もちろん GDI の API群 を使ってビットマップハンドルを介してピクセルデータに文字を描いたり線を引いたりもできます。

最後に以降の説明で頻繁に使う言葉を定義しておきます。

上で説明しましたように、パレットをサポートするビデオモードでは DDB はパレットと組で使わなくてはなりません。この DDB を「パレットを使う DDB」と呼ぶことにします。同じように、1 bpp/4bpp/8bpp の DIB Section、つまり カラーテーブルのインデックスをピクセル値として持つ DIB Section を「カラーテーブルを使う DIB Section」 と呼ぶことにします。

もう一つ、すでに 4 bpp など bit per pixel という言葉を気軽に使っていますが、これは1ピクセルを何ビットで表現するかを現します。これを「色ビット数」と呼ぶことにします。尚、正確にゆうと、1ピクセルに入っている色情報のビット数の数え方には2通りの考え方が有ります。1ピクセルが占有するビット数を重要視する考え方と、1ピクセルが表現する色情報のビット数を重要視する考え方です。例えば、BitFields 形式の DIB Sectionでは1ピクセルが 16bit を占める場合でも、ピクセルが表す RGB 値は 15bit の場合が有ります。これを 15 bpp と呼んだりすることが有りますが、以下の説明では1ピクセルが占有するビット数を重要視し これも 16 bpp と呼ぶことにします。

2.7.2 TBitmap の機能

TBitmap は基本的にはビットマップを保持するためのクラスですが、単純な ビットマップのコンテナではなく、ビットマップを操作するためのさまざまな機能を持っています。列挙すると以下のようになります。

ビットマップへの描画

TBitmap は Canvas プロパティ を持ち、ビットマップに自由に描画できます。TBitmap はパレット持てるため、デフォルトパレット以外の色を指定できます。

 

ビットマップファイルの読み込み。ビットマップファイルへの書き込み。

TBitmap はビットマップをファイルから読み込んだり、ファイルへ書き出したりできます。リソース中の ビットマップの読み込みも出来ます。TBitmap は Win3.x のビットマップファイル形式と PM 1.x のビットマップファイル形式をサポートしています。 Win3.X 形式では 1/4/8/24 bpp、Delphi 3.XJ ではこれに加えて 16/32 bpp もサポートしています。

TBitmap はビットマップを読み込むとき、ビットマップ(DIB) のカラーテーブルからパレットを自動的に抽出します。但し残念ながら、True Color のビットマップ(DIB)は通常パレットを持たないのでパレットは作られません。従って、256色のビデオモードで TrueColor のビットマップを Windows 9X/ME できれいに表示ことはですません。但し、TBitmap にはハーフトーンモードでビットマップをスクリーンに描画する機能が有り、Windows NT ではきれいに表示されます)。

クリップボードとの入出力

TBitmap はクリップボードへ DDB とパレットを渡したり、クリップボードから DDB とパレットを取り込むことが出来ます。残念ながら CF_DIB 形式のビットマップはサポートされていません。CF_BITMAP 形式しかサポートしないため、TBitmap のクリップボードとの入出力時におけるビットマップの品質は、その時のビデオモードに依存してしまいます。例えば 256 色のビデオモードで TrueColor の画像をクリップボードを介して正常な色で受け取ることはできません。

 

ビットマップの変形、モノクロ化

TBitmap はビットマップの内容を保ったまま、ビットマップの大きさを変更したり、ビットマップをモノクロ化することが出来ます。

 

他のキャンバスへの描画

オフスクリーンビットマップのイメージをディスプレイや、他のオフスクリーンビットマップへ描画できます。

 

ビットマップの設定と取り出し

TBitmap に他で作成した ビットマップのハンドル やパレットをセットすることが出来ます。また、TBitmap 内から、ビットマップハンドル やパレットを取り出すことが出来ます。

 

ビットマップの形式の変更

TBitmap は TBitmap 内のビットマップの形式を変更できます。DDB に加えて 任意の非圧縮の DIB Section 形式に変換が可能です。RLE 圧縮はできません。

 

透明色のサポート

ビットマップのイメージをディスプレイや、 他のオフスクリーンビットマップへ描画する際、透明色を指定してビットマップの一部を透明にできます。

 

Scanline プロパティ の提供

ビットマップのイメージに高速にアクセスできるように、DIB Sectionのスキャンラインへのメモリポインタが提供されています。

2.7.3 TBitmap の構造

TBitmap の基本的な内部構造は 以下のようになっています。

図 2.7-5 TBitmap に基本的な構造

TBitmap のビットマップの実体やパレットは TBitmapImage が持っており、複数の TBitmap で共有されるようになっています。最初 TBitmap は一つの TBitmapImage しか持ちませんが Asign メソッドで TBitmap をコピーすると TBitmapImage はコピーされず参照カウントのみが増えます。

ビットマップやパレットに何か変更を加える場合、TBitmap は TBitmapImage をコピーして共有を解除します。いわゆる Copy On Write 方式になっています。これは TBitmap の利用するメモリ量を減らすのに使われています。

var bm: TBitmap;
  :
  :
  bm := TBitmap.Create;
  bm.LoadFromFile('xxx.bmp');
  :
  :
  Image1.Picture.Graphic := bm;
  bm.Free;
  :
  :

上のコードでは TBitmap を イメージコントロール に代入してから、TBitmap を破棄しています。

単純に考えると Image1.Picture.Graphic プロパティに TBitmap を代入すると、TBitmapの Assign メソッドが呼ばれ TBitmap のコピーが代入されるので一時的にビットマップが2枚できるようにお思えます。しかし、実際には Assign メソッドは TBitmapImage の参照カウントが増やすだけなのでメモリの使用量は増えず、TBitmap 型の変数 bm が破棄されると、ビットマップはコピーされること無く イメージコントロールに渡ることになります。このテクニックはすべての TGraphic の派生クラスで使われていますが、特にメモリの使用量が大きい TBitmap では重要です。

TBitmapImage の内部構造は 以下のようになっています。

図 2.7-7 TBitmapImage の内部構造

図 2.7.8 TDIBSection 構造体

TBitmap は  ビットマップを保持するのに DDB/DIB Section  ハンドルで保持しますが、 加えてビットマップのさまざまなデータを保持するのに Win32 の DIBSECTION 構造体を使っています。また、透明描画を行うため、マスクビットマップのハンドルも保持しています。これはビットマップの一部の透明にして描画するために必要なモノクロビットマップのハンドルです。

2.7.4 TBitmap の描画用メソッドとその問題点

2.7.4.1 パレット

TBitmap へ図形を描画するには Canvas プロパティ を用いますが、この時 TBitmap の Palette プロパティ が重要な役割を果たします。

TBitmap は 内部にパレットを持つことができます。Palette プロパティ に論理パレットハンドルを代入したり、カラーテーブルを持つビットマップを読み込んだり、クリップボードからパレットを持つビットマップを受け取ったりしたするとパレットが TBitmap の中に格納されます。このパレットは TBitmap の Canvas を使うと自動的に Canvas に選択/実体化します。

ビットマップ形式が 「パレットを使う DDB」または「カラーテーブルを使う DIB Section」の場合、ビットマップの色は TBitmap の Palette プロパティ が示すパレットで決まります。

ビットマップ形式が 「パレットを使う DDB」または「カラーテーブルを使う DIB Section」の場合、 Windows が用意するデフォルトの20色以外の多彩な色を使って描きたいときは、パレットを用意して TBitmap の Palette プロパティ にあらかじめ代入しておく必要が有ります。ただし、LoadFromStream/LoadFromFile メソッドなどでビットマップを読んだ場合や、クリップボードからビットマップを受け取った場合は、そのビットマップのパレット(カラーテーブル)が TBitmap に自動的にセットされます。

TBitmap は特にパレットを設定しない場合、Windows が用意したデフォルトパレットを使います。

2.7.4.2 Canvas(TBitmapCanvas)

TBitmap の Canvas は ビットマップに描画するためのものですが、その実体は TBitmap 内に有る メモリデバイスコンテキストです。ビットマップはこのデバイスコンテキストに選択されてから描画されます。

TBitmap の Canvas(TBitmapCanvas) は TBitmap の Palette プロパティ のパレットを自動的にデバイスコンテキストに選択/実体化します。

2.7.4.3 Draw メソッド

TBitmap の Draw メソッドは Protected なメソッドで Canvas の Draw/StrechDraw メソッドから呼び出されます。例えば

procedure TForm1.Button1Click(Sender: TObject);
var bm : TBitmap;
begin
  bm := TBitmap.Create;
  bm.LoadFromFile('XXX.bmp');
    :
    :
  Canvas.Draw(0, 0, bm); // または StretchDraw(...
    :
    :
end;

とした場合、ビットマップがフォーム上に描かれますが、この時 TBitmap の Draw メソッドが呼び出されます。Draw メソッドは

  procedure Draw(ACanvas: TCanvas; const Rect: TRect);

という形式で、ACanvas は描画先の Canvas をあらわします。

Draw メソッドはスクリーンデバイスに描画することを想定しているため、描画先の Canvas(デバイスコンテキスト)に TBitmap のパレットを選択/実体化してから Canvas にビットマップを描画します。描画する際  StretchBlt で DIB ならば StretchDIBits で描画します(TransParent 描画の場合はちょっと複雑ですが、基本は StertchBlt です)。スクリーンデバイスの色ビット数が 8bpp 以下で、表示するビットマップの色ビット数がスクリーンデバイスの色ビット数より大きい場合、ハーフトーンモードで描画します。ただし Windows 95 では OS のバグのためハーフトーンにはなりません。NT でのみ有効になります。

Draw メソッドは描画先の Canvas がスクリーンやプリンタなら問題ありません。しかし 描画先が TBitmap の Canvas で TBitmap が DDB を保持している場合問題が起きます。

2.7.1に書いたように DDB はパレットの FGマッピングに依存しています。従って、ビットマップに描画する際はそのビットマップのパレットが DDB が選択されているメモリデバイスコンテキストに選択されていなければなりません。しかし、TBitmap の Draw メソッドは描画先の Canvas のデバイスコンテキストに自分のパレットを選択してしまいます。したがって、「パレットを使う DDB」をTBitmap が保持している場合、その TBitmap の Canvas の Draw/StretchDraw メソッドは、描画元と描画先の TBitmap がパレットを持たない場合以外では使えません。色化けが起きます。Draw/StretchDraw でビットマップをコピーする前に、描画先の TBitmap が DIB Section か確認して下さい。DIB Section かどうかは HandleType プロパティ で判定できます。描画先の TBitmapを強制的に DIB にするには Dormant メソッドを呼ぶか HandleType プロパティ に bmDIB を代入してください。

図 2.7-9 Canvas.Draw によるビットマップのコピー

描画先の TBitmap が 「カラーテーブルを使うDIB Section」 の場合もう一つ注意することが有ります。ビットマップはカラーテーブルの中に有る色しか表現できませんから、描画先のビットマップは描画元のビットマップを十分に表現できるだけのカラーテーブル(パレット)をもつ必要が有ります。完全に同じカラーテーブルを持つ必要はありません。例えばカラーテーブル(パレット)内の色の順番が異なっていても大丈夫です。描画の際に Windows がその辺は面倒を見てくれます。一番簡単なのはあらかじめ


  var bm1, bm2: TBitmap;
    :
    :
    :
    bm2.Palette := CopyPalette(bm1.Palette);
    :
    :
    bm2.HandleType := bmDIB;
    bm2.Canvas.Draw(0, 0, bm1);
    :
    :

などとすることでしょう。これなら完全に色化けなくコピーできます。もちろんこれは一例で、例えばいろいろなカラーテーブル(パレット)を持つ複数の画像を合成するような場合ハーフトーンパレットをセットしておくのも一つの解でしょう。どのようにカラーテーブル(パレット)をセットしておくかはアプリケーションが何を求めているかに依存します。

2.7.4.4 Canvas の CopyRect メソッドと TBitmap

ビットマップをコピーするもう一つの方法として Canvas の CopyRect メソッドが有ります。Canvas の Draw/Stretch メソッドとは異なり、CopyRect は描画先にパレットを選択しません。しかし、TBitmap の Canvas で CopyRect を使うには注意点が有ります。以下はビデオモードがパレットをサポートしていることを前提に話を進めます。

CopyRect メソッドはビットマップをコピーするのに StretchBlt API を使います。DDB 間で StretchBlt を使うのは危険が伴います。CopyRect は描画元と描画先の矩形領域を指定してビットマップをコピーしますが、256色モードの場合、DDB 内の色情報(システムパレットインデックス)がそのままコピーされます。TBitmap はそれぞれが独立にパレットを持つ設計になっているので、TBitmap のインスタンス毎にパレットを持っており、パレット毎にその FGマッピング は独立なので、これらが一致していないと色化けが起こります。したがって、描画先と描画元が 共に 「パレットを使う DDB」 コピー元の TBitmap がパレットを持つ場合は、描画元、描画先のパレットが同じ色の並びをもつものでない限り Canvas の CopyRect メソッドは使えないのです。

描画元、あるいは描画先にDIB Section を使えばこの問題は簡単に回避できます。

当然ですが、コピーの際、Draw メソッドのときと説明した通り CopyRect でも描画先の ビットマップが「パレットを使う DDB」か「カラーテーブルを使う DIB Section」の場合は、適切なパレット(カラーテーブル)をあらかじめセットしておく必要が有ります。

図 2.7-10 Canvas.CopyRect によるビットマップのコピー

2.7.5 ビットマップの大きさ

TBitmap 内のビットマップの大きさは Width/Height プロパティ で決まります。TBitmap が Create された直後は両方とも 0 で TBitmap 内にビットマップはありません。Width/Height を共に0より大きくすると TBitmap は白色の DDB を作ります。

Width/Height プロパティ の大きさを変更すると TBitmap はビットマップ画像の内容を保ったままビットマップの大きさを変更します。

図 2.7-11 Width/Height プロパティ の変更

上の図はビットマップの Width/Height プロパティ の変更の様子をあらわしています。

2.7.7 モノクロ化

カラービットマップを保持する TBitmap のインスタンスの MonoChrome プロパティ を True にすると、TBitmap は保持するビットマップを DDB のモノクロビットマップに変換します。カラーからモノクロに変換する際、TBitmap が DDB を保持している場合はCanvas.Brush.Color(背景色) と同色のピクセルは '1' に、それ以外のピクセルは '0' に変換されます。ブラシの色が基準になる点を注意してください。

残念ですが Delphi 2.0J にはバグが有るらしく、モノクロ化の際 ビットマップが DIB Sectionの場合、白紙のモノクロビットマップが出来てしまいます。

Delphi 3.0J ではモノクロ化の際 ビットマップが DIB Sectionの場合でもモノクロ化が可能ですが、DDB の場合とは動作が異なります。

DDB の場合は上述のように Canvas.Brush.Color(背景色)に基づいてモノクロ化が行われましたが、DIB の場合は色の明るさに基づいて白黒2色に減色することによりモノクロ化します。Canvas.Brush.Color は無視されます。

DDB をモノクロ化するとき一点重要な注意点が有ります。DDB をモノクロ化するために Canvas の Brush.Color プロパティ に色を設定するとき、Canvas がパレットを持っており、ビデオモードがパレットをサポートしていて、 Brush.Color に Windows の予約色以外の色を指定する場合は、RGB 型の色を設定してはいけません。PaletteIndex 型か PaletteRGB 型で指定してください。そうしないと背景色は Windows の予約色の中から選ばれるため、ほとんどの場合モノクロ化がうまく行きません。注意してください。

2.7.7 LoadFromStream、 SaveToStream メソッドなど

TBitmap の LoadFromStream と SaveToStream は TBitmap とストリームとで DIB を入出力します。入出力先をファイルストリームにしたのが LoadFromFile/SaveToFile で、入力元をリソースストリームにしたのが LoadFromResourceName/ LoadFromresourceID です。

LoadFromXXXX メソッド群は Windows 3.X 形式と PM1.X 形式のビットマップファイルをサポートします。Windows 3.X 形式では、 1/4/8/16/2432 bpp、をサポートします。SaveToXXXX メソッド群が出力する形式は Windows 3.X 形式のみです。

LoadFromStream/loadFromFile/LoadFromResourceName/LoadFromResourceID メソッドは基本的に TBitmap にビットマップを読み込むだけなのですが、一点注意する点が有ります。RLE 圧縮ファイルの取り扱いです。

LoadFromXXXX は DIB 形式のビットマップをファイルやストリームやリソースなどから取り込みますが、常にそのまま TBitmap の中にそのまま置けるわけではありません。TBitmapは DIB を DIB Section に変換して保持しようとします。ところがDIB Section は 圧縮形式の DIB には使えません。そこでどうするかというと、TBitmap は DIB を DDB に変換して保持するのです!!。

試しに 4 bpp RLE 圧縮形式のビットマップファイルを TBitmap に読んで HandleType プロパティ を覗いてみてください。bmDDB になっているはずです。つまり ビットマップをファイルから読んだからといって TBitmap が DIB Section を保持しているとは限らないのです。注意してください。

この影響で、Delphi 4 までは イメージコンポーネントやボタンのグリフなどを設計時に RLE 圧縮ファイルから読み込んだ場合、圧縮が解除されてしまうのでフォームァイルが非常に大きくなってしまいました。特に ビデオモードが TrueColor の場合 24 bpp でセーブされるため最悪の事態になりました。Delphi 5 以降でも基本的な事情は同じなのですが、Delphi 5 以降の TBitmap はRLE圧縮ファイルを読んだ場合、DDB に加えて RLE DIBのイメージ も保持し、ビットマップに変更が加えられるまでは RLE DIB イメージを保持しつづけます。そして RLE DIB イメージが保持されている場合、フォームファイルには RLE DIB イメージを書き込むので、フォームファイルが膨れ上がることはありません。

2.7.8 クリップボードとの入出力

TBitmap の LoadFromClipboardFormat/SaceToClipBoardFormat はクリップボードと TBitmap のインスタンスとの間で ビットマップとパレットを入出力するためのメソッドです。このメソッド自体はクリップボードと入出力するわけではありませんが、Clipboard 変数を使って

  var bm: TBitmap;
    :
    :
  bm := TBitmap.Create;
    :
    :
  bm.Assign(Clipboard);
    :
    :
  Clipboard.Assign(bm);
    :
    :

などとした場合に自動的にこれらのメソッドが呼び出されます。

LoadFromClipBoardFormat メソッドはパラメータで指定された DDB とパレットを TBitmap のインスタンス内にセットします。Handle プロパティ や Palette プロパティ にセットするのと似ていますが、このメソッドはコピーを取ってからセットします。

SaveToClipboardFormat メソッドは LoadFromClipBoardFormat とは逆に、TBitmap のインスタンスから DDB とパレットを取り出すメソッドです。ReleaseHandle や Releasepalette propety とは異なり、DDB と パレットのコピーを作って取り出します。ようするに、この2つのメソッドは、TBitmap の DDB 用の入出力メソッドです。残念ながら CF_DIB 形式のクリップボードデータはサポートしていません。

2.7.9 Palette プロパティ と Handle プロパティ

TBitmap の Handle プロパティ と Palette プロパティ は、TBitmap のインスタンス内の ビットマップ やパレットのハンドルの参照したり、逆に ビットマップ やパレットを TBitmap のインスタンス内に設定するのに使います。

Handle プロパティ に ビットマップハンドルを代入した場合、ビットマップ のハンドルが TBitmap のインスタンス内に取り込まれます。Handle プロパティ に代入した ビットマップ は、TBitmap で管理されることになり、TBitmap が破棄される時に自動的に削除されます。従って、別途削除することはできなくなります。Palette プロパティ も同様です。

逆に TBitmap のインスタンスの管理下に有る ビットマップ や パレットを外へ取り出すには ReleaseHandle メソッドと ReleasePalette メソッドを使います。これらのメソッドを使うと、ビットマップ やパレットは TBitmap の管理下から離れ、TBitmap のインスタンスとは関係なくなります。これらのメソッドを実行すると、 TBitmap のインスタンス内の ビットマップはなくなります。

パレットの場合はもう少し複雑で、ReleasePalette が呼ばれたときに TBitmap 内のビットマップが DDB なら TBitmap 内からパレットは無くなり、DDB は Windows のデフォルトパレットを使うようになります。

TBitmap 内のビットマップが DIB Section の場合は DIB Section とカラーテーブルを引き離すことはできないのでパレットがなくなる事はなく、ReleasePalette メソッドで引き取られたパレットとは別のパレットが TBitmap 内に自動的に再生成されます。

2.7.10 ビットマップ形式の変更

TBitmap の PixelFormat プロパティを使うとビットマップの形式を変更できます。

ビットマップ形式の変更では古い形式のビットマップの内容が新しい形式のビットマップにコピーされますが、以下のようになります。
変換先の形式変換の概要
DDBスクリーン互換の DDB を作りそこにビットマップイメージをコピーする。パレットは旧形式のビットマップのパレット(カラーテーブル)を引き継ぐ。
1bpp DIB Sectionカラーテーブルが White(Color Index = 0)/Black(Color Index = 1) の 1bpp DIB Section を作りそこに 旧 イメージ の内容をコピーする。
4bpp DIB Section カラーテーブル(パレット)が Windows の 予約色(16色)になっている 4bpp DIB Section を作りそこに 旧 イメージ の内容をコピーする。
8bpp DIB Section カラーテーブル(パレット)が スクリーンデバイスのハーフトーンパレットになっている 8bpp DIB Section を作りそこに 旧 イメージ の内容をコピーする。
15/16/24/32 bpp DIB Section 15/16/24/32 bpp DIB Section を作りそこに 旧 イメージ の内容をコピーする。カラーテーブル(パレット)は旧形式のビットマップのパレット(カラーテーブル)を引き継ぐ。

重要な点は 1/4/8 bpp DIB Section への変換ではカラーテーブル(パレット)が固定の色構成になることです。また最適化された減色処理なども行われません。従ってたとえ色ビット数が増える方向の変換でも画像が劣化しますので注意してください。

蛇足ですが、この形式変換は Delphiのヘルプに全くといって良いほど記載がありません。ヘルプの TBitmap の PixelFormat プロパティ の説明は全く意味不明です。改善を望みます。

2.7.11 透明色を持つビットマップ

TBitmap はビットマップ透明色を指定して描画できます。このように描画できるのは TBitmap の Draw メソッドだけなので、描画先の Canvas の Draw/StretchDraw メソッドで TBitmap を描画したときにのみ透明な部分を指定した描画ができます。CopyRect ではできません。

図 2.7-12 透明色使ったビットマップの描画例

透明な部分を指定するため、ビットマップはマスク用の モノクロ DDB を保持します。TBitmap は Transparent プロパティ が True で Draw メソッドが呼ばれると(Canvas.Draw/StretchDraw で TBitmap を指定すると)自動的に 保持しているビットマップと TransparentColor プロパティ を使ってマスク用の 1bpp DDB を生成し描画します。このビットマップのハンドルは MaskHandle プロパティ で参照できます。

TBitmap は保持しているビットマップの内容が変化するとマスク用の 1 bpp DDB を自動的に無効にするので、Canvas.draw/StretchDraw で TBitmap を描画するとき、TBitmap は常に保持しているビットマップの内容を反映した正しいマスクを作り、それを使って描画します。Mask 用のビットマップは TBitmap が必要に応じて作るものですから通常は MaskHandle プロパティ を参照してもあまり意味はありません。TBitmap から Mask 用 DDB を取り出すには ReleaseMaskHandle メソッドを使います。

TransparentColor プロパティ は透明になる色を表します。デフォルトではビットマップの左下の色(モノクロビットマップでは clWhite)が使われますが、TransparentColor に clDefault 以外の色を代入するとその色を使うようになります。面白い事にモノクロ化のときの Brush.Color の設定とは異なり、TransparentColor プロパティ にセットする色は PaletteIndex 型や PaletteRGB 型にする必要はありません。clWhite や clBtnColor をそのまま設定できますし、RGB(255,255,255) などのような RGB 型もそのまま設定できます。PaletteRGB 型への変換は TBitmap 側で行っているからです。

TransparentColor プロパティ に clDefault を代入すると、Transparent プロパティ を読み出すとビットマップの左下色(モノクロビットマップでは clWhite) になります。TransparentMode はTransparentColor が自動的に選ばれるか、指定された色が使われるかを示します。TransParentMode に tmAuto を代入する事は TransparentColor に clDefault を代入する事と等価です。tmFixed を代入すると直前の TransparentColor の値に TransparentColor プロパティ の値が固定されます。逆に TransParentColor に clDefault を代入すると TransparentMode は tmAuto になり、clDefault 以外を代入すると TransParentMode は tmFixed になります。

Transparent プロパティ の機能とは直接関係ありませんが、TBitmapのMask メソッドは TransparentColor プロパティ を「背景色」に指定してビットマップをモノクロ化します。このメソッドは Monochrome プロパティ に True を代入するのと背景色の選び方以外はほとんど同じです。しかし Monochrome プロパティ とは違い、TBitmap が DIB Sectionを保持している場合でも、DDB を保持している場合と同じ結果が得られます。TransparentColor に RGB型の色指定を行っても問題有りません。なぜなら、透明色を使った描画のときと同様 TransparentColor は TBitmap 内部で PaletteRGB 型の色に直されてから使われるからです。

2.7.12 Scanline プロパティ

Delphi 3以降 の TBitmap は DIB Sectionを積極的にサポートしましたが、TBitmap の Canvas の Pixels プロパティ を介してビットマップのピクセルにアクセスする限り、アクセスに非常に時間がかかります。スピードを謀ってみると TBitmap が DDB を保持しているときよりも DIB を保持しているほうが遅くなり、私の現在使っている S3 Virge + Pentium 133MHz ではなんと1ピクセル読み込むのに20μSecもかかってしまいます。

TBitmap が DDB を保持している場合は Pixels プロパティ でアクセスするほかに手はないのですが、TBitmap は DIB  Section のピクセルデータのアクセスするためのメモリポインタを提供してくれます。これを使えば Windows の GDI サブシステムを介さずにピクセルにアクセスできるので、ピクセル単位のビットマップの処理を飛躍的に速くすることができます。

TBitmap の Scanline プロパティ は DIB の指定したピクセル列へのポインタを返す プロパティ です。

図 2.7-13 Scanline プロパティ

上図からわかるようにスキャンラインは横一列分のピクセル群のことで、Scanline プロパティ はその先頭アドレスのポインタを提供します。Scanline プロパティ は配列 プロパティ で添え字はスキャンラインの位置を表わします。y=0 はもっとも上のスキャンラインを表わし、y=(TBitmap の高さ Heightプロパティ)-1 はもっとも下のスキャンラインを表わします。ScanLine を参照すると TBitmap は保持しているビットマップが DDB なら自動的に DIB Sectionに変換しますが、これは一時的な変換しかしないので、Scanlineプロパティを使うときは、必ず使う前に TBitmap が DIB Section を保持するようにしてください。変換は HandleType プロパティか PixelFormat プロパティでできます。

Scanline プロパティ はピクセルにアクセスするためのメモリポインタを提供してくれるわけですが、DIB Section内の生データにアクセスするわけですから DIB Sectionのピクセルフォーマットについての知識が必要です。DIB は色ビット数などの形式の違いによってピクセルデータのフォーマットが変わります。詳しくは Win32 programmer's Reference を参照してください。

一点だけ問題点を述べておきます。TBitmap にはピクセルフォーマットを決定するのに必要な情報を プロパティ で完全には提供していません。若干不足しています。従って、Scanline プロパティ を有効に使うには残念ながら Windows の API に若干お世話になる必要が有ります。贅沢かもしれませんが、DIB Section のカラーテーブルにアクセスするために Colors プロパティ をつけるとか 16 bpp/32bpp の DIB Section で使用されるビットフィールドマスクなどが参照する プロパティ をつけるなどの工夫が欲しかったと思います。

2.7.13 まとめ

最後に TBitmap の機能を簡単にまとめておきます。

TBitmap はビットマップを保持し各種の操作を行うためのクラスです。TBitmap はその内部に ビットマップ、パレット、メモリデバイスコンテキストを内蔵し、ビットマップに関する各種の操作を極めて簡略化してくれます。
TBitmap はストリーム・ファイル・リソース・クリップボードとビットマップを入出力できます。
TBitmap はビットマップの大きさの変更をビットマップの内容を保ったまま変更できます。またビットマップをモノクロ化することもできます。
TBitmap 内のビットマップへ Canvas プロパティ を使って自由に描画できます。
Width/Height プロパティ を変更することで TBitmap 内のビットマップの内容を保ったままビットマップの大きさを変更できます。
TBitmap は ビットマップの形式を内容を保ったまま変更できます。
TBitmap はビットマップの一部の色を透明色に指定できます。描画の際、透明な部分を描画しないようにすることができます。
TBitap はビットマップの個々のピクセルに高速にアクセスするためにスキャンラインへのポインタを提供します。

ここで、一点問題点を一つ追加しておきます。ビットマップが 16色のパレットを持つ場合 TBitmap は常に VGA Color のパレットを作ります。つまり、 DDB は常に VGA Color の DDB になり、DIB はカラーテーブルとパレットで色が食い違うことになります。簡単に言えば、TBitmap は 16色 のビットマップを VGA Color に勝手に減色するのです。おそらくボタンの Glyph などが不要に色を奪い合うのを防ぐのが目的だとは思いますが、それは IgnorePalette プロパティ を True にすれば十分なはずで非常に困った仕様です。改善を望みます。

 

戻る ホーム 上へ 進む inserted by FC2 system