C言語というか、画像処理の話なのですが、ご意見頂ければ幸いです。
ペイントやPhotoshopのような画像加工ソフトを作ろうとしています。
よくあるレイヤー機能を持たせたく、実装してみましたが、レイヤービットマップの合成
(アルファブレンド)処理が遅くて、もっと速くならないかと悩んでます。高速化のために、
効果的な方法はないでしょうか?
環境:WinXP Home (SP3)、VC++2008EE、C言語、Win32API
今の実装(※1)は、レイヤーが何枚かある状態で、ユーザが「レイヤーの透明度を変更」
「レイヤーの可視・不可視を切り替え」するたびに、
1) 全レイヤービットマップ(32bit)をなめて合成ビットマップ(24bit)を作る
2) 合成ビットマップを画面に表示
しています。
画像が大きいほど、レイヤーの枚数が多いほど、1) 合成ビットマップを作る処理 が
どんどん重くなっていきます。(CPU: Core Duo 1.6GHz)
・2000x2000ピクセルのレイヤー2枚合成するのに、約0.2秒
・4000x4000ピクセル 〃 、約0.7秒
・5000x5000ピクセル 〃 、約1.2秒
・ 〃 3枚合成するのに、約1.3秒
・ 〃 4枚合成するのに、約1.4秒
一方、私は「SAI」という5千円くらいのペイントソフトを使っていますが、このソフトは
同様の操作が、比べものにならないくらい高速なんです。大きな画像でも、レイヤーが
何枚あっても、「レイヤー透明度の変更」や「レイヤー可視・不可視の切り替え」は
一瞬でおわります。私の実装からすると信じられない速さです。
無い知恵を絞って、「浮動小数点演算は使わない」「乗除算を減らす」「除算はビットシ
フトで代替」などのC言語的な小手先の?高速化は施したつもりですが、まったくSAIとい
うソフトの足元にも及びません。
SSEやアセンブラによる高速化はちょっと敷居が高くて未経験なのですが、「画像が大き
いほど、レイヤー枚数が増えるほど遅くなる」という傾向を感じなくする程の効果がある
のかどうか・・?もっと、根本的なデータ構造やアルゴリズムが違うのではないか・・?
「1つのレイヤー透明度を変えただけで全部のレイヤーをなめているから遅い」のかな?
ただ、レイヤーの合成結果をキャッシュしておく?よい方法も思い浮かびません。
なにか定石的なアルゴリズムなどあればよいですが、見つかりません。
大きな画像になるほど合成に時間がかかるのは仕方ない、とも思うのですが、SAIという
ソフトにはそのような傾向は微塵も感じられません。不思議すぎます・・。
という感じなのですが、画像処理ソフトのレイヤー機能の実装方法について、高速化の
ためのよい方式などはないでしょうか?よろしくお願いします。
(※1)現在のレイヤー合成処理の概要
・レイヤー1枚には、32bit(RGBA)DIBデータ1つ、透明度値(0~255)1つがある。
・ビットマップの先頭から最後まで1ピクセルずつ、
・上層レイヤーから下層レイヤーに向かって、
・ピクセル透明度とレイヤー透明度を考慮した係数を、レイヤーピクセルRGB値に乗算し、
・合成ビットマップ(24bitDIB)ピクセルRGB値に加算していく。
・コードの雰囲気は以下のような感じです。(実際はもっとごちゃごちゃしてます)
/* レイヤー1つの型(片方向リスト用) */ struct LayerNode { DIB32 Bitmap; /* 32bitビットマップ(RGBA)データ */ BYTE Alpha; /* レイヤー透明度(0~255) */ BOOL Visible; /* 可視・不可視状態 */ struct LayerNode *next; /* 次(下)のレイヤー */ }; /* 合成ビットマップ作成 */ for( n=0; n<=ビットマップ最終ピクセル位置; n++ ) { R = G = B = 0; /* 合成ビットマップピクセル値 */ for( layer=最上層レイヤー; layer; layer=layer->next ) { if( !layer->Visible || !layer->Alpha ) continue; /* 不可視・完全透明 */ 係数 = ピクセル透明度(layer->Bitmap->Pixel[n].A)と レイヤー透明度(layer->Alpha)を考慮して算出 R += 係数 * layer->Bitmap->Pixel[n].R G += 係数 * layer->Bitmap->Pixel[n].G B += 係数 * layer->Bitmap->Pixel[n].B } 合成ビットマップ->Pixel[n].R = R; 合成ビットマップ->Pixel[n].G = G; 合成ビットマップ->Pixel[n].B = B; } /* 画面表示 */ StretchDIBits相当の処理( 合成ビットマップ );