Allegro の 半透明処理

  Transparency and patterned drawing(原文)
http://alleg.sourceforge.net/onlinedocs/en/index020.html#drawing_mode
をもとに検証。

グラフィック描画モード

描画モードを設定する
void drawing_mode(int mode, BITMAP *pattern, int x_anchor, int y_anchor);
int modeには、DRAW_MODE (グラフィック描画モードを)セットする。
これらのモードは幾何学関数、 putpixel ,line,円,ポリゴン,塗り等の描画に影響する。
text, blitting,スプライト描画には影響しない。
  DRAW_MODE_SOLID - デフォルト, 実際の色を描画
DRAW_MODE_XOR - XOR演算 描画 同じ図形を二度描くと消える(通常より処理が重くなる)
*pattern にBITMAPストラクチャを指定すると、描画時にその画像で敷き詰められる。
このとき、そのBITMAPストラクチャをdestroy してはいけない。
また、パターン化するビットマップの縦横サイズはそれぞれ 2のベキ乗pixelでなければならない、
例えば 64x16pixel のパターンはOK)、パターン描画が必要なければ NULLを指定できる。
パターン描画のオフセット座標は x_anchor, y_anchor で指定される。この値はデフォルトでは(0,0)左上角である。
DRAW_MODE_COPY_PATTERN - マルチカラーパターンで敷き詰める(カラー画像)
DRAW_MODE_SOLID_PATTERN - シングルカラーパターンで敷き詰める(255.0.255は白、それ以外の色をベタ塗り)
DRAW_MODE_MASKED_PATTERN - マスクされたパターンで敷き詰める(255.0.255を透過し、それ以外の色ベタ塗り)
DRAW_MODE_SOLID_PATTERN と、DRAW_MODE_MASKED_PATTERN でベタ塗りされる色は
幾何学関数の描画色パラメータ(上の例では makecol(R,G,B)) に依存する。
 image_area = load_bitmap("penguin.bmp",0);//ペンギン画像
drawing_mode( DRAW_MODE_COPY_PATTERN , image_area , 0, 0);//描画モードとパターン設定。
rectfill( screen ,x, y ,x1, y1 ,makecol(255, 255,0) );// ペンギン模様の長方形が描かれる。
半透明描画を設定
DRAW_MODE_TRANS - 半透明処理を有効にする
アルファチャンネルによる半透明処理を行うにはこのモードを有効にしなければならない。
このモードを有効にした後、color mapping table または blender関数を呼び出した後で
描画関数を使えば半透明処理されたパターンを描画できる。ただし、blendの種類によっては処理が重くなることがある。
詳細については、半透明処理に関する blender関数を見ること。
image_area = load_bitmap("penguin.bmp",0);//パターン画像
drawing_mode(DRAW_MODE_TRANS,NULL , 0, 0);// 描画パターンと描画モード設定
set_trans_blender(0, 0 ,255 ,127 );//ブレンド方法
rectfill( screen ,x, y ,x1, y1 ,makecol(255, 255,0) );// 半透明の長方形が描かれる。

描画モードを DRAW_MODE_SOLID に戻せばまた元のように描画できる。

各モードの描画結果
(drawing_mode関数で設定したモードに基づいて描画)


半透明処理について

半透明処理は256色モードと1677万色モードの2つのタイプが存在する。
1677万色モードの半透明処理のほうが機能がまとめられているぶん扱いやすいです。

256色モードの半透明処理

color_map = &light_table;
color_map はAllegro側で定義済のポインタで、カラーマップテーブルを扱う。

void create_trans_table(COLOR_MAP *table, const PALETTE pal, int r, g, b, void (*callback)(int pos));
特定のカラーマップに、パレットに基づいた半透明効果を付加する。
カラー1 と カラー2 をこのテーブルによって結合する場合、2色の中間色が出力される。 RGB パラメータはそれぞれ固有の範囲をもっている。
255 :不透明
128 :透明度は 50%
0  :完全に透明となる。

これには例外があり、ソースの#0 番目の色は特殊でこの変更が適用されない。 そのため透過色のあるスプライトは正しく表示することができる。 コールバックが指定して場合は、計算中に256回呼ばれ描画が進行する。NULLが指定されていれば、コールバックしない。
create_light_table(&light_table, pal, 0, 0, 0, NULL);/*ライティング効果を定義 */
void create_light_table(COLOR_MAP *table, const PALETTE pal, int r, g, b, void (*callback)(int pos));
特定のカラーマップについて、パレットに基づいた照明効果を付加する。 カラー1 と カラー2 があって、 カラー1 が 0-255段階 照明効果テーブルをもって結合する場合。 ライティングレベルについて
  255  :カラー2 には変化はない。
 1-254 : カラー1とカラー2の中間色を出力する。
  0   :この関数で指定したRGB の色で出力される。
RGB 変数は、それぞれ 0-63 の範囲をとる。 コールバックが指定して場合は、計算中に256回呼ばれ描画が進行する。NULLが指定されていれば、コールバックしない。
create_trans_table(&trans_table, pal, 128, 128, 128, NULL);/* 透明効果の定義 */
void create_color_table(COLOR_MAP *table, const PALETTE pal, void (*blend)(PALETTE pal, int x, int y, RGB *rgb), void (*callback)(int pos));
特定のカラーマップについて、指定した色 で バレットで満たす。 カラーマップ ブレンド関数と一緒に使われる場合には、色の組み合わせを決定する為に呼び出される。 ブレンドすべき2つのパレットを指定し、RGBストラクチャは、0-63 の範囲をとる。Allegro は厳密にマッチする色が見つからなくてもそれに近い色を探す。 コールバックが指定して場合は、計算中に256回呼ばれ描画が進行する。NULLが指定されていれば、コールバックしない。

void create_blender_table(COLOR_MAP *table, const PALETTE pal, void (*callback)(int pos));
特定のカラーマップについて、指定したバレットで満たす。 この関数によって作成された256色のマッピングテーブルは、1677万色モードであっても同じように利用できる。 つまり、このテーブルを1677万色モードの set_trans_blender()や set_blender_mode()関数 で使う事ができる。

1677万色モードの半透明処理

概要
1677万色モードにおいて半透明処理とライティング処理の機能は次のような blender 関数にまとめて実装されている。
unsigned long (*BLENDER_FUNC)(unsigned long x, y, n);
描画されるべきピクセル毎に、これら関数に色情報が、2つのカラーパラメータ x,y に渡され R,G,B成分に分解される。 それを数学的変換に従い結合し、合成された単一の色情報を描画先のピクセルへ返される。

xパラメータは 混合する色を、yパラメータはベースとなる色を表す。値は 0-255 の 範囲を持ち透明度をコントロールする。 半透明処理を扱う関数を使って描画される時 ソースとなる色 x 、ビットマップに描画される色 y、アルファ-レベル(透明度) の値 n が 半透明処理のモード(乗算や差の絶対値など)でセットされた関数に渡される。なお、この関数へRGBのデータをもってくることはできない。

litスプライトの描画時に半透明処理関数が使われる場合には、 ソースとなる色 x は ブレンディングモードでセットされた 関数に渡されたRGBの3つの項 として表される。(この関数に渡された アルファ-レベルは考慮されない) y は スプライトの色、 n は描画関数自身に渡された アルファレベルの値。

これらの関数が、複数の異なったカラーモードから使われるかもしれないので、3つのコールバックが存在している。
1)、15-bit 用の 5.5.5 pixels,
2)、16 bit 用の 5.6.5 pixels,
3)、24-bit 用の 8.8.8 pixels(これは、24,32bitカラーでも同じように格納されたbitデータを 共有できる。)
半透明処理を有効にする
void set_alpha_blender();
1677万色 RGBAスプライトを描画する場合に、特殊なアルファチャンネル ブレンドモードを有効にする。 この関数を有効にした後、1677万色モードの画像を描くために draw_trans_sprite() または draw_trans_rle_sprite()関数を使う。 アルファ値は、もとになる画像から取得される。このため画像の各部分について透明度を得る事ができる。 このモードが有効になっている間は、他の半透明処理 関数を利用する事はできない。

もし他の1677万色 RGBAスプライトを使うならば、描画する前に別の描画モード(例えば set_trans_blender() など)に切り替えなければならない。

半透明スプライトを使う
void draw_trans_sprite(BITMAP *bmp, BITMAP *sprite, int x, int y);
void draw_trans_rle_sprite(BITMAP *bmp, const RLE_SPRITE *sprite, int x, int y);
ブレンド効果は通常はblitting image やスプライトには適用されない。 もし画像やスプライト自体を半透明化したい場合は、この関数を使用して描画することで半透明効果を得られる。

半透明ブレンド表示をするには、あらかじめ set_alpha_blender();を初めに”一回だけ”呼び出しておくことで有効になる。 set_alpha_blender();を呼ばずに、これらのスプライト関数を使用しようとするとエラーで落ちる。

draw_stans_spriteのブレンド効果は、呼び出されたset_****_blender()関数のパラメータ に依存する。 そのため、他の処理で別のブレンドモードに切り替えたことを忘れていると、再びスプライトを表示した時に思った表示にならない場合がある。 だからこまめにブレンドモードの切り替えを行う(使い終わったら元のモードに戻すなど)必要がある。

半透明化は、draw_trans_sprite()関数で描画された時だけなので、元のスプライトを使いたい場合は通常のdraw_sparite関数を使えば問題ない。 マニュアルには、半透明処理が重くなるのでできればビデオRAMに直書きをおすすめすると書かれている。

何も負荷をかけてない場合は 120 FPS。


半透明スプライトは、RLEスプライトと通常のスプライト関数に対応していますが、
ここでは、RLEスプライトを使う方法で説明する。(使い方はほぼ同じ)

RLEスプライトを使用する方法
通常の画像をスプライトをRLEスプライトとして使用するには一行足すだけで済む。 しかし、get_rle_sprite でRLEスプライトを定義するタイミングを間違えるとプログラムは強制終了する。 これは、 blitして画像データを転送した後のタイミングでRLEスプライトを定義すると問題なく動作する。
 BITMAP *tako_pat[4];

 drawing_mode(DRAW_MODE_SOLID, NULL , 0, 0);

 //タコ画像→スプライト用領域へコピー
 int i;
  for(i=0;i<3;i++){ ;
   tako_pat[i]= create_bitmap(25,33);
   blit(image, tako_pat[i] , 26*i, 0 ,0,0 ,25,33);//画像を転送。普通ならここで終わり。
   tako_pat[i]=get_rle_sprite(tako_pat[i]);//このタイミングで RLEスプライトを定義すると問題ない。
 }

 drawing_mode(DRAW_MODE_TRANS, NULL , 0, 0);//半透明モードを有効にする
 set_alpha_blender();//半透明スプライトを有効に(1回のみ呼ぶ)
 set_trans_blender(0, 0 ,255 ,127 );// ブレンド方法を決定(何度でも切り替え可能)
表示用の関数はRLEスプライト対応のものを使う。
 draw_trans_rle_sprite( buffer, tako_pat[pat], tako[j].x, tako[j].y );//RLEスプライト用の関数
 destroy_rle_sprite(tako);//プログラム終了する前に RLEスプライトのメモリを解放
//(通常スプライトの場合は、 draw_trans_sprite関数 , destroy_bitmap関数 を使う)
RLEスプライトと 通常のスプライトの処理速度は、ほとんど差がなかった。 テストプログラムで半透明処理(スクリーン)タコを増やし負荷をかけていった場合 60FPS に達したのはどちらも 156匹 を表示した時だった。この2つのスプライトの違いはメモリ消費にあると思われる。

スプライトの移動するスピードを比較してみるとCompiled Sprite は RLE Sprite や Sprite に比べてはるかに 高速だった、 しかし超大量のキャラクター表示&移動&アニメーションさせると、どれも同じような結果に終わってしまった。 DRAW_MODE_SOLID で 等倍スプライト表示、 1832〜1839匹で 63〜64FPS に達した。

256色スプライトの合成
void set_write_alpha_blender();
特殊アルファチャンネル編集モードを有効にして、1677万色 RGB スプライトをRGBA フォーマットに変換し、その上に描画するに。 この関数が呼ばれると、描画モードを、DRAW_MODE_TRANS にして、1677万色画像に、カラー値(0-255)を書き込む事ができる。 この時に値を書き込むと 色データ自体には改変は行われず、アルファデータが付加されることになる。 draw_trans_sprite() 関数を呼び出せば、1677万色スプライトの上に256色スプライトを重ね合わせることもできる。


各種ブレンドモード関数
色の結合演算について (がくのぼめぱげ)を参考にしました。描画タイミングは 1loop毎 に行っています。
透過演算
void set_trans_blender(int r, int g, int b, int a);
lit truecolor pixels または 半透明処理 のための線形補完 (linear interpolator)ブレンドモードを有効にする。
加算
void set_add_blender(int r, int g, int b, int a);
半透明混合 または lit truecolor pixels のためのadditive blender mode を有効にする。
比較(暗)?焼き込み?
void set_burn_blender(int r, int g, int b, int a);
ソースとなる色の明度(lightness)を下げて暗くする。
カラー
void set_color_blender(int r, int g, int b, int a);
色相(Hue)と彩度(Satulation)を変化させる。 輝度(Luminance)には影響はない。
差の絶対値
void set_difference_blender(int r, int g, int b, int a);
ソースとなる色と、描画先となる色の間で計算を行い描画する
ディザ合成
void set_dissolve_blender(int r, int g, int b, int a);
dissolve blender mode を有効にする。 ソースと描画先の色をランダムに置き換える。置き換えられたピクセルの透明度は、アルファ値に影響する。テレビの砂嵐のようなアニメーション効果。
覆い焼きカラー
void set_dodge_blender(int r, int g, int b, int a);
ソースの明度 を 描画先の色に適用する。 白が最も影響を受け、黒は影響を受けない。
色相
void set_hue_blender(int r, int g, int b, int a);
ソースの色相(Hue)を 描画先の色に適用する。
反転
void set_invert_blender(int r, int g, int b, int a);
ソースの色の反対色を 描画先の色に適用する。
輝度
void set_luminance_blender(int r, int g, int b, int a);
ソースの色の輝度(Luminance)を 描画先の色に適用する。
乗算
void set_multiply_blender(int r, int g, int b, int a);
ソースと描画先の色を混合する。白い部分があればは変化せず、黒い部分があればより暗くなる。
彩度
void set_saturation_blender(int r, int g, int b, int a);
ソースの色の彩度(Satulation)を 描画先の色に適用する。
スクリーン
void set_screen_blender(int r, int g, int b, int a);
multiply blender関数とは逆の効果を得る (Photoshopの スクリーン と同じ?)

ブレンドモードのセット
void set_blender_mode(BLENDER_FUNC b15, b16, b24, int r, g, b, a);
void set_blender_mode_ex(BLENDER_FUNC b15, b16, b24, b32, b15x, b16x, b24x, int r, g, b, a);
使用するブレンダー関数を、それぞれの色モード別に指定する。

ブレンダーベンチマーク

負荷をかけた状態でブレンド処理結果を比較してみた。 今回のペンチマークでは、1677万色 モード、スクリーンサイズは600x400 で音楽を再生しながら画面をリアルタイム書き換えた。 描画タイミングは 8 loop 毎。ブレンド処理 には 50%透過 ( RGBA(0,0,0,127)) というパラメータで行った。
(なお色深度や、描画分け、アルゴリズムによっては結果が変わります)

上の画像では分かりにくいですが、リアルタイムにブレンド効果を行った場合、 set_dodge_blender では描画色が時間につれて変化したり、 set_dissolve_blender ではテレビの砂嵐ノイズ のようにアニメーションした。 デフォルトでは 110〜124FPS である場合。

高速だったもの。
set_trans_blender 111 FPS
set_multiply_blender 103 FPS

体感速度が低下したもの。34FPS
set_color_blender
set_hue_blender
set_saturation_blender
set_luminance_blender
処理するタイミングを工夫する。描画待ちを行うと体感的にも遅くなる。例えば バッファからスクリーンへ描画する場合 8loop毎だと動きがカクカクしてしまうため、スムーズにアニメーションさせるには1loop毎に描画する。

set_dodge_blender
set_invert_blender
set_dodge_blender
上の3種類はFPS表示が消えてしまっているが、処理スピード的には問題はなかった。
その他は、80〜96 FPS を記録した。


透過メッセージウインドウなど使えそうなもの
set_multiply_blender
set_screen_blender
set_add_blender
set_dissolve_blender
 
Last modified 2005.04.24

Tsukubado