戻る ホーム 上へ 進む

2.6 パレット

本節では、Delphi のグラフィックスでサポートの比較的弱い部分であるパレットに関して解説します。パレットは複雑な色の図形を表示するのに不可欠なものですが、残念ながら Delphi はパレットを充分にサポートしていません。 ここでは、Delphi がパレットをどのように扱うかを解説します。

2.6.1 パレットについて

Delphi のパレットを理解するには、Windows のパレットとパレットマネージャの動きを理解する必要が有ります。残念な事に、マイクロソフトは PSDK などでパレットを充分に説明していません。その結果、用語すらちゃんと決まっていないありさまなので、まず最初にパレットマネージャの仕組みについてかなり深く解説することにしました。

2.6.1.1 システムパレット

Figure 2.6-1

図 2.6-1 システムパレット

システムパレットとはパレットを持つビデオカード内の物理的なパレットのコピーと考えてよ良いものです。両者は適宜シンクロナイズされています。システムパレットは固定数のエントリを持つ色値(RGB値)の配列で、それがそのまま物理パレットの各エントリの色を表しています。システムパレットのエントリ数は16から256までの任意の値をとりえますが、20以下ではパレットマネージャが機能しないため、20を超えるエントリを持つものとします。また現実には、大きさは通常256エントリ(色)ですので、以下の説明ではシステムパレットの大きさは256エントリ(色)とします。

システムパレットの先頭の10エントリと最後の10エントリはシステムで予約されています。この予約エントリは、ボタンの色やウィンドウのタイトルバーの色などに使われます。通常このエントリの色は変化しないため、Windows の利用者はボタンの色が突然変わったりして驚いたりすることがありません。残りの236エントリはアプリケーションが自分の欲しい色を設定できる部分です。

システムパレットの各エントリには状態を表すフラグがついています。状態は Static, Used, Unused の3種で、Static はパレットマネージャが色を変更できないエントリ、つまり予約エントリを表し、Used はパレットマネージャが色を割り当て済のエントリ、Unused は色が割り当てられていないエントリを表します

2.6.1.2 論理パレット

Figure 2.6-2

図 2.6-2 論理パレット

論理パレットはアプリケーションがパレットマネージャに対し色を要求するための要求書で、アプリケーションが欲しい色が RGB 値の配列の形で格納されています。厳密には、各エントリには、要求をより細かく指定するため、PC_RESERVED などのフラグがついていますが、説明がややこしくなるので、全てのフラグが指定されていないものとして以下を説明します。パレットアニメーションはもう時代遅れだと思うので、詳しく知りたい方はMSDNをご覧ください。

2.6.1.3 パレットの実体化

論理パレットの要求に基づいて、パレットマネージャがシステムパレットのエントリに色を割り当て、システムパレットのエントリ番号と論理パレットのエントリ番号との対応表(マッピング)を作成する作業をパレットの実体化といいます。

2.6.1.4 フォアグラウンドパレット(FGパレット)とバックグラウンドパレット(BGパレット)

フォアグラウンドパレット(以降FGパレット)はアクティブなトップレベルウィンドウ、またはその子孫(子ウィンドウ、孫ウィンドウ、曾孫ウィンドウ...)の Device Context に SelectPalette のパラメータを ForceBackground = False にして選択されている論理パレットです。バックグラウンドパレット(BGパレット)は以上の条件を満足しないスクリーン互換の Device Context に選択された論理パレットです。スクリーン互換の Memory Device Context に選択されているパレットもBGパレットです。FGパレットは色の獲得に関し最も高い優先権を持ちます。

アクティブウィンドウやその子ウィンドウにそれぞれ別のパレットを SelectPalette API のパラメータ を ForceBackground = False にして選択すると、FGパレットが複数存在するようになることに注意してください。これは避けなければなりません。

2.6.1.5 フォアグラウンド実体化(FG実体化)とバックグラウンド実体化(BG実体化)

フォアグラウンド実体化(以降FG実体化)は FGパレットの実体化。バックグラウンド実体化(以降BG実体化)はBGパレットの実体化です。

2.6.1.6フォアグラウンドマッピング(FGマッピング)とカレントマッピング

Figure 2.6-3

図 2.6-3 マッピング

マッピングとはシステムパレットのエントリ番号と論理パレットのエントリ番号の対応表で、パレットの実体化により作られます。フォアグラウンドマッピング(以後FGマッピング)とはパレットが「最初に実体化」される時に作られ「パレットの中に記憶される」マッピングで、実体化がFG実体化なのかBG実体化なのかにかかわらず作成されます。一度FGマッピングが作成されると、パレットが破棄されるか、パレットを UnRealseObject API で「非実体化」するまでFGマッピングは変化しません。

繰り返しますが、FGマッピングは必ずパレットが作成された直後の最初の実体化で作られます。パレットがFGパレットかBGパレットかは関係ありません。スクリーンデバイス互換の Memory Deviec Context にパレットを実体化してもFGマッピングは作られます。FGマッピングの作成はシステムパレットに影響を与えません。FGマッピングの作成は以下のような手順になります。

まず、現在のシステムパレットのコピーを作り、その予約エントリ以外の状態をすべて Unused にします。次に、論理パレットの各エントリに対してシステムパレットのコピーを使って以下の処理を行います。

  1. 論理パレットのエントリに入っている RGB 値を吟味し、システムパレットの Static なエントリの RGB 値と Used なエントリの RGB 値を全て調べて、一致するエントリがあればそのエントリを論理パレットのエントリと対応づけます。
  2.  1. で適当なエントリが無ければ、適当な Unused なシステムパレットエントリを選んで、そのエントリに 論理パレットエントリから RGB 値をコピーし、そのエントリを Used にして論理パレットのエントリを対応づけます。
  3. Unused なエントリが無ければ、論理パレットのエントリの RGB 値にもっとも近い RGB 値を持つシステムパレットのエントリを選び、そのエントリと論理パレットのエントリを対応づけます。

カレントマッピングはやはりシステムパレットのエントリ番号と論理パレットのエントリ番号の対応表ですが、パレットが実体化するたびに作成し直されるマッピングです。カレントマッピングの作成はパレットがFGパレットかBGパレットかで全く違います。また、カレントマッピングの作成は本物のシステムパレットを変更します。カレントマッピングの作成はパレットが実体化される時、パレットがFGパレットでもBGパレットでも行われます。従って、パレットが作成され最初に実体化される時はカレントマッピングとFGマッピングが同時に作られ、以後の実体化ではカレントマッピングだけが更新されます。

FGパレットのカレントマッピングの作成は単純です。まず本物のシステムパレットの予約エントリ以外の状態を Unused にし、FGマッピングをカレントマッピングにコピーし、論理パレットの各エントリに対応する本物のシステムパレットのエントリが Unused なら、そのエントリに論理パレットのエントリの RGB 値をコピーして Used にします(下図参照)。

つまり、FGパレットがFG実体化する時、色のマッチング作業は行われず、FGマッピングに従って論理パレットのRGB値がダイレクトにシステムパレットに送り込まれます。FGマッピングは一度作成されると変化しないので、FGパレットが実体化する時、FGパレットのエントリとシステムパレットのエントリの関係はいつも同じになります。

Figure 2.6-4

図 2.6-4 FGパレットのカレントマッピングの作成

BGパレットのカレントマッピングの作成アルゴリズムはFGマッピングのアルゴリズムと基本的には同じですが念のため以下に記述します。

BGパレットのカレントマッピングの作成では、論理パレットの各エントリに対して以下の処理を行います。

  1. 論理パレットのエントリに入っている RGB 値を吟味し、システムパレットの Static なエントリの RGB 値と Used なエントリの RGB 値を全て調べて、一致するエントリがあればそのエントリを論理パレットのエントリと対応づけます。
  2.  1. で適当なエントリが無ければ、適当な Unused なシステムパレットエントリを選んで、そのエントリに 論理パレットエントリから 本物のシステムパレットのエントリに RGB 値をコピーし、そのエントリを Used にして論理パレットのエントリを対応づけます。
  3. Unused なエントリが無ければ、論理パレットのエントリの RGB 値にもっとも近い RGB 値を持つシステムパレットのエントリを選び、そのエントリと論理パレットのエントリを対応づけます。

BGパレットのカレントマッピングの作成はマッピングを行う前にシステムパレットの予約エントリ以外のエントリの状態を Unused には「しません」。BGパレットはスクリーン互換の Memory Device Context に選択されたパレットも含みます。

2.6.1.7 色の配分のシナリオ

Windows はトップレベルウィンドウがアクティブになると、そのウィンドウに WM_QUERYNEWPALETTE を送ります。アクティブなトップレベルウィンドウが色を獲得したい場合は、このメッセージに応答し、適切なパレットを選んでウィンドウのデバイスコンテキストにフォアグラウンドで選択し、実体化しなければなりません(つまりFG実体化する)。ここでさらにいくつかのパレットをBG実体化してもかまいませんが、あまり好ましくは有りません。後述するように、FG実体化が WM_PALETTECHANGED メッセージを送る場合、その後にBG実体化をしても既に色が他のウィンドウに取られてしまっているため、時既に遅しになってしまっているからです。また、やってはいけないので、ここで複数のパレットをFG実体化することです。これをやると、パレットが色を奪い合い、結局最後にFG実体化したパレットのみが色を正常に獲得し、それより前のFG実体化が無意味になってしまいます。

Windows はFGパレットの実体化の結果、システムパレットのエントリのRGB値の変更を検出すると、すべてのトップレベルウィンドウと壁紙に WM_PALETTECHANGED を送ります。送る順番は、アクティブウィンドウ、現在のアクティブウィンドウがアクティブになる前にアクティブだったウィンドウ、さらに前にアクティブだったウィンドウ、...壁紙という順です(下図参照)。

Figure 2.6-5

図 2.6-5 アクティブウィンドウが変わる時のパレット関連メッセージの流れ

アクティブウィンドウは通常この WM_PALETTECHANGED を無視しなければなりませんが、VCL では独特の手法で応答します(後述)。また、他のトップレベルウィンドウは、色を獲得したければ、BG実体化行います(アクティブでは無いのでBG実体化しか出来ません)。以上から、色はまず、WM_QUERYNEWPALETTE メッセージ内でアクティブなトップレベルウィンドウに獲得され、次に WM_PALETTECHANGED で、前にアクティブだったウィンドウ、その前にアクティブだったウィンドウ...壁紙という優先順位で確保されます。

WM_QUERYNEWPALETTE の処理で RealizePalette が0以外の応答を返してきた場合、これはシステムパレットが変更されたことを表していますから、アクティブウィンドウやその子孫のウィンドウは InvalidateRect 等を呼んで自ウィンドウの書き直しを Windows に促さなくてはなりません。

各トップレベルウィンドウ、およびその子孫のウィンドウは WM_PALETTECHANGED でパレットのBG実体化を行った後、RealizePalette の戻り値を見て、自分を再描画すべきか判断します。戻り値が0以外なら、InvalidateRect 等を呼んで、Windows に自ウィンドウの再描画を Windows に促さなくてはなりません。

各ウィンドウは WM_PAINT メッセージの応答時など、描画を行う場合は、パレットを BG実体化し描画を行います。BG実体化を使うのは、描画時にアクティブウィンドウ、およびその子孫のウィンドウで FG実体化を使うと、せっかく作り上げた色の配分が壊れてしまうからです。

各トップレベルウィンドウは WM_QUERYNEWPALETTE や WM_PALETTECHANGED 応答時に実体化したパレットは 描画時に実体化するものと同じとは限りません。WM_QUERYNEWPALETTE/WM_PALETTECHANGED 応答時のパレットはそのトップレベルウィンドウが欲しい全ての色を要求しているのかも知れないし、トップレベルウィンドウの子孫のウィンドウのパレットかもしれないし、トップレベルウィンドウに表示される一部の図形用のパレットなのかもしれないからです。WM_QUERYNEWPALETTE や WM_PALETTECHANGED での応答時に実体化するパレットは、取り敢えず欲しい色を要求しているに過ぎません。どのように実体化するかはアプリケーションの判断に任されています。

2.6.1.8 アプリケーション側が積極的に色の配分を変えたい時。

アプリケーションが動作中に、必要な色が変わる時があります。例えばビットマップファイルを読むと、そのカラーテーブルの色が欲しくなります。WM_QUERYNEWPALETTE メッセージのシナリオはアクティブウィンドウが切り替わる時しか働きません。アプリケーション側で欲しい色が変化した場合、アプリケーションは積極的に色の再配分を要求しなければなりません。

このような場合、アプリケーションはパレットを ForceBackGround = False でパレットを選択して実体化して色を要求します。パレットをアクティブウィンドウに選択したのでなければ、これはBG実体化ですから、そのパレットには現在のシステムパレットの状況に応じて、それなりに色が割り当てられるだけです。一方、アクティブウィンドウおよびその子孫のウィンドウで実体化した場合はFG実体化になりますから、WM_QUERYNEWPALETTE 応答時と同じメッセージの流れが発生し(つまり WM_PALETTECHANGED が発生し)、色が再配分されます。

2.6.1.9 ウィンドウへの図形の描画

ウィンドウの Device Context にパレットが実体化している場合、ウィンドウへの Rectangle や Line の描画は、システムがパレットをサポートしている場合は、予約エントリの20色か、パレットに割り当てられたシステムパレット上の色を使います。色がどのように選ばれるかは色の指定の仕方や、何の色かによって変わります。

パレットをサポートしているシステムでは
 色を RGB値で指定色を PaletteRGB で指定色を PaletteIndex で指定
ペンの色
テキストの色
予約エントリの20色から最も近い色のエントリが使用される。最も近い色を持つ論理パレットのエントリに対応するシステムパレットのエントリが使用される。論理パレットのエントリ番号とシステムパレットのエントリ番号の変換にカレントマッピングが利用される。論理パレットのインデックス値で指定されたパレット上のエントリに対応するシステムパレットのエントリが使用される。論理パレットのエントリ番号とシステムパレットのエントリ番号の変換にカレントマッピングが利用される。
ブラシ色予約エントリ20色を使ったディザ同上同上

パレットをサポートしていないシステムではシステムパレットのエントリ番号では無くRGB値で描画されるわけですが、ついでにこれについても書いておきます。
 色を RGB値で指定色を PaletteRGB で指定色を PaletteIndex で指定
ペンの色
テキストの色
RGB値をそのまま使用する。指定された RGB値をそのまま使うインデックス値で論理パレットの RGB 値を取得
ブラシ色RGB 値をそのまま使うか、指定されたRGB値を利用可能な全ての色を使ってディザで表現RGB 値をそのまま使うか、指定されたRGB値を利用可能な全ての色を使ってディザで表現同上

となります。

2.6.1.10 オフスクリーンビットマップへの描画

オフスクリーンビットマップへの描画は オフスクリーンビットマップが DDB の場合は基本的にはウィンドウ(スクリーン)への描画と同じです。但し、システムパレット番号の選択は、Memory Device Context に実体化しているパレットが仮想的にウィンドウ(スクリーン)にFG実体化しているとみなして行われます。つまり、パレットをウィンドウ(スクリーン)にFG実体化して描画する時と同じシステムパレットエントリ番号でビットマップに描画します(重要!)。

オフスクリーンビットマップが DIB の場合(つまりメモリデバイスコンテキストに DIB Section が選択されている場合)は色の指定法と描画される色の関係が DDB とはかなり異なります。

2.6.1.11 オフスクリーンビットマップをスクリーン(ウィンドウ)にコピー

これを説明する前に、ビットマップのコピーで使われる BitBlt API と StretchBlt API の処理について触れる必要があります。SDK の説明ではこの2つはパレットに関して異なる処理をするというような記述がありますが、実は全く同じです。但し、ビットマップをコピーする時に、ビットマップが変形するかしないかで処理が全く違ってきます。BitBlt はコピー先の領域の大きさが指定できず、コピー元と同じ大きさが仮定されますが、これはあくまで論理座標上の話で、デバイス座標上ではマッピングモードが異なれば大きさが異なることがあります。BitBlt/StretchBlt はコピー元の領域の大きさとコピー先の領域の大きさがデバイス座標上で異なるかどうかで処理を切り替えます。大きさが変わる場合を「変形有り」、変わらない場合を「変形無し」とします

オフスクリーンビットマップをスクリーンに「変形無し」でコピーする場合、パレットをサポートしていないシステムではビットマップ内のピクセル値(RGB値)をそのままスクリーンにコピーするだけですが、パレットをサポートしているシステムでは、オフスクリーンビットマップのピクセル値がシステムパレットのエントリ番号なので簡単にはコピーできません。

オフスクリーンビットマップ内のピクセル値(システムパレット番号)は前述したように FGマッピングを使って書き込まれているので、オフスクリーンビットマップの Memory device Context に実体化しているパレットのFGマッピングとカレントマッピングが異なる場合、そのままシステムパレット番号をコピーできません。そこで、BitBlt/StretchBlt は Memory Device Context に実体化しているパレットのFGマッピングとカレントマッピングから、システムパレット番号の変換表を作ります。ビットマップ内のピクセル値(システムパレットエントリ番号)はこの変換表で変換してからスクリーンに描き込まれます。

オフスクリーンビットマップをスクリーンに「変形有り」でコピーする場合は全く処理が異なります。まず、BitBlt/StretchBlt は Memory Device Context に実体化されているパレットのFGマッピングを使って、ビットマップのピクセル値を論理パレットのエントリ番号に直し、さらに論理パレットを見て RGB 値を得ます。次にウィンドウ(スクリーン)に実体化している論理パレットのエントリの中から最も近い色のエントリを選び、カレントマッピングに従ってそれをシステムパレット番号に変換してウィンドウ(スクリーン)に描き込みます。

2.6.1.12 ビットマップ間のコピー

オフスクリーンビットマップ間のコピーは、オフスクリーンからスクリーンへコピーする場合と少し異なります。まず、Windows 9X/ME で「変形無し」の場合、及び Windows NT系列のWindows の場合、システムパレット番号の変換は一切行われず、そのままコピーされます。

一方、Windows 9X/ME で「変形有り」の場合は、オフスクリーンからスクリーンへコピーする場合と良く似た処理をしますが、コピー先の Memory Device Context に実体化されたパレットが仮想的にスクリーン(ウィンドウ)にFG実体化しているとみなしてコピー処理を行います。

2.6.2 VCL はパレットをどう使うか?

長々と VCL とは直接関係ないことを描いてきましたが、そろそろ、VCL がパレットをどにように使うかを説明しようと思います。

2.6.2.1 パレットを使う契機

2.6.1 で解説しましたように、パレットを使うアプリケーションは以下の4つの場合にパレットをどう実体化するかを決めなければなりません。

  1. WM_QUERYNEWPALATTE の応答時
  2. WM_PALETTECHANGED の応答時
  3. 描画時
  4. アプリケーションで欲しい色が実行時に変化した時

どのようにそれぞれ応答するかはアプリケーションの自由ですが、VCL にはそれなりのポリシーがあります。

一般にトップレベルウィンドウは複数の子ウィンドウを持っていますが、子ウィンドウがそれぞれ異なるパレット(色の要求)を持つことがあります。また、子ウィンドウが無くても、トップレベルウィンドウ上にパレットが異なる複数の画像を描かなくてはならない時があります。そういう場合、トップレベルウィンドウはこれらの色の要求を調停する必要があります。いろいろな調停法が考えられますが、主なものは以下の二種です。

  1. 全ての色を取得
    トップレベルウィンドウ、およびその子孫のウィンドウの全ての色要求を満たす大きなパレットを作って、パレットマネージャに要求する。完全主義。
  2. 代表者を決める
もっとも優先順位の高い描画対象を決め、その部分のパレットを代表として、パレットマネージャに要求する。最優先の描画対象はうまく描画されるが、残りの部分は色の品質が多少落ちても我慢する。

もちろん、この中間の高度な妥協を行うやり方も考えられますが、VCL のデフォルトの動作は 2 です。

2.6.2.2 PaletteChanged メソッド と GetPalette メソッド

VCL では、コントロールがパレットを持ちます。フォームもコントロールの一つです。パレットマネージャへの色の要求は、まず、色に関して最優先のコントロールを決めることから始まります。つまり、WM_QUERYNEWPALETTE でFG実体化するのはそのフォームの最優先コントロールのパレットです。

フォームの PaletteChanged メソッドには2つの機能があります。一つは、パラメータを True で呼び出す場合です。PaletteChanged(True) はフォーム、およびフォームに属するコントロールの中から、最優先コントロールを選び、ForceBackGround = False 、でパレットを選択し実体化します。つまり、フォームがアクティブな場合、最優先コントロールのパレットをFG実体化します。

PaletteChanged(True) はどのように最優先コントロールを決めるのでしょうか? 以下に PaletteChanged の使っているアルゴリズムを解説します(下図参照)。

Figure 2.6-6

図 2.6-6 PaletteChangedの探索ルート

PaletteChanged(True) はまずフォームがパレットを持っているかを確認します。各コントロールには GetPalette メソッドが有り、GetPalette メソッドが 0 以外を返せばそのコントロールはパレットを持っています。フォームがパレットを持っていればフォームが最優先コントロールです。

もしフォームがパレットを持っていなければ(それが普通です)、フォームのコントロール配列を最後尾から見てゆきます。コントロール配列とは Parent プロパティ がそのフォームになっている、つまりフォームが親のコントロールが入っている配列です。配列の最後尾には Z-Order が最も手前のコントロールが入っています。最後尾から順に調べてゆき、最初にパレットを持っているコントロールを最優先コントロールに選びます。調べてゆく途中に コントロール配列を持つコントロール(TWinControl の派生クラス)があれば、さらに再帰的にそのコントロールが親になっているコントロールも調べにゆきます。

結局、PaletteChanged(True) はコントロールの親子関係の階層で、フォームに階層が近く、階層が同じならば、最も Z-Order が手前の、パレットを持つコントロールを選びます(フォーム自身が最も近いので、フォームがパレットを持てばフォーム自身が選ばれます)。

例えば、フォームにイメージコントロールを1個だけ貼り付け、イメージコントロールにビットマップファイルが読み込みこんだ場合は、イメージコントロールは Picture.Bitmap.Palette プロパティ を GetPalette メソッドで返すので、PaletteChanged(True) はイメージコントロールを最優先コントロールとして選びます。

PaletteChanged(True) はもしパレットを持つコントロールが見つからなければ何もしません。

PaletteChanged メソッドを パラメータを False にセットして呼び出した場合、 PaletteChanged(Flase) は上図に示した探索リストに従って、パレットを持つコントロールを探し、パレットを持つコントロールのパレットを全てBG実体化します。フォーム内の全てのパレットを持つコントロールのパレットを実体化することに注意してください。

以上の説明は普通のフォームの場合ですが、MDI の親フォームの場合は、PaletteChanged メソッドの探索ルートが少し違います。まずアクティブな MDI Child を調べ、次に フォームの MDIChildren 配列の中の MDI Child を全て調べてから、自分自身、配下のコントロールという順で調べます(下図参照)。

Figure 2.6-7

図 2.6-7 PaletteChanged の探索ルート(MDI 親フォームの場合)

2.6.2.3 WM_QUERYNEWPALETTE と WM_PALETTECHANGED への応答

フォームが WM_QUERYNEWPALETTE を受け取ると PaleteChanged(True) を呼び出します。つまり、フォームは最優先コントロールのパレットをFG実体化し、色を獲得します。

一方、フォームが WM_PALETTECHANGED を受け取ると PaletteChanged(False) を呼び出します。最初に WM_PALETTECHANGED を受け取るのはアクティブウィンドウなので、まず、フォームの最優先コントロールのパレットがBG実体化され、残りのコントロールのパレットもBG実体化されます。

アクティブウィンドウの応答処理が終わると、次に直前にアクティブだったトップレベルウィンドウが WM_PALETTECHANGED を受け取り、それが Delphi のプログラムなら PaletteChanged(False) で全てのパレットを持つコントロールのパレットをBG実体化します。こうして全てのトップレベルウィンドウが WM_PALETTECHANGED に応答します。

以上の処理で、まずアクティブウィンドウのコントロール群が探索ルートの順に色を獲得し、次に直前にアクティブだったフォームのウィンドウのコントロール群が色を獲得し、さらに次のフォームが、という具合に色が獲得されてゆきます。

VCL の処理の特徴は、アクティブウィンドウの色の獲得を WM_QUERYNEWPALETTE と WM_PALETTECHANGED の2回に分けて行うところです。この方法は WM_PALETTECHANGED の処理が一本化出来るので一見よさそうですが、実は問題があります(後述)。

2.6.2.4 描画。

VCL では 描画処理は各コントロールに任されています。パレットを持つコントロールはイメージコントロールなど極少数ですが、描画時のパレットの扱いかたは同じで、自分のパレットをウィンドウのデバイスコンテキストにBG実体化して描画を行います。

2.6.2.5 アプリケーションで欲しい色が実行時に変化した時の処理

VCL でフォームが欲しい色が実行時に変化するのは以下の場合です。
  1. フォーム内のコントロールの Z-Order が変化した場合。
  2. アクティブな MDI 子フォームが変更された場合
  3. コントロールのパレットが変更された場合(例えばイメージコントロールに新しい画像をセットした場合)。

1 と 2 は VCL が自動的に PaletteChanged(True) を呼び出して処理します。3 はコントロール側で対応しなくてはなりませんが、例えばTImage(イメージコントロール) は自動的に PaletteChanged(True)を呼び出します(実際にはWM_QUERYNEWPALETTEをフォームに送っています)。

2.6.2.6 VCL のパレット制御の問題点

VCL のパレット制御は簡単に述べれば、最優先コントロールのパレットを WM_QUERYNEWPALETTEの処理 でFG実体化し、全てのコントロールのパレットを WM_PALETTECHANGED の処理でBG実体化するという方式です。

この方式の問題点は、WM_QUERYNEWPALETTE での 最優先コントロールのパレットのFG実体化が WM_PALETTECHANGED を送ることが保証されない点です。WM_PALETTECHANGED が送られるのは、システムパレットが一エントリでもFG実体化で書き換えられた場合だけです。たまたまシステムパレットの状態が最優先コントロールのパレットのFGマッピングと一致していた場合、WM_PALETTECHANGED は送られず、従って、アクティブウィンドウの他のコントロールのパレットは実体化される機会がありません。システムパレットの変更が起きなければ、アクティブウィンドウ全体の再描画も起きませんから、アクティブウィンドウ内の最優先コントロール以外は、不正な色で表示されることになります。Delphi を使って作ったイメージコントロールを多用するアプリケーションを、バックグラウンドで描画するアプリケーションといっしょに動かすと、時々 Z-Order が奥のイメージコントロールの色の激しく劣化するのはこのためです。

2.6.3 まとめ

2.6.2 で説明しましたように、VCL はコントロール単位でパレットを扱います。パレットはウィンドウではなく、コントロール単位で実体化されます。

VCL は同一ウィンドウ上に有る複数のコントロールの色の優先順位を決め、色の配分を自動的に調停します

フォームも含め、Delphi のコントロールでパレットを使うには

  1. GetPalette を実装して自分がパレットを持っていることを VCL に報せる。
  2. 描画時に 自分のパレットをBG実体化して描画する。
  3. 自分のパレットが変更された時は、フォームの PaletteChanged(True)を呼ぶかフォームに WM_QUERYNEWPALETTE メッセージを送る。
の3点を実装すれば、少なくともそのコントロールが最優先コントロールならば、色が正しく表示されるようになります。コード例が Delphi Tipsに有りますのでご活用ください。

戻る ホーム 上へ 進む

inserted by FC2 system