第CB回 PhotoShopのレイヤ合成アルゴリズムについて(YGAフォーマットを提唱する) 00/10/18
ついにやりました!!アンチェリ転送に成功しました!
yaneSDK2ndのサンプル12で公開しようと思っているのですが、アンチェリ転送に成功しました。ここで、簡単に原理を説明します。
まず、画像からαチャンネル(PhotoShopで言えば「不透明度」)情報を抽出しなくてはなりません。PhotoShopで言えば、PNGの書き出しでそれを行なえますが、PhotoShopは非圧縮PNG書き出しが出来ないので、これを扱うのは非常に不便なのです。
非圧縮PNGについては、さ〜さんのページに資料的価値の高い解説文が存在します。しかし、たかがαチャンネルを抜き出すためだけに、わざわざPNGの読み込みルーチンを作るのは苦痛です。仮にそれがlibpngで簡単に作れるとしても、今度はPNG書き出しが出来ないツールだと大変です。PNGを使わず、そして、もっと簡単になる方法は無いでしょうか?これが今回の課題でした。
そこで、考えたのは、PhotoShopのレイヤ合成のアルゴリズムについてです。私が見る限り、PhotoShopのレイヤ合成は、エネルギー分配に基づくもので、一番手前のレイヤにまず100%のエネルギーが与えられます。一番手前のレイヤの不透明度が仮に60%だとすれば、そのレイヤに60%のエネルギーが割り振られ、残りの40%のレイヤが後段のレイヤに行きます。後段の(2番目に手前の)レイヤが仮に75%の不透明度であれば、この40%のエネルギーの75%が、2番目に手前のレイヤに割り振られます。つまり、40%×75%=30%ぶんです。さらに後段のレイヤでは、残った10%のエネルギーが割り振られます。これを、エネルギーが0になるまでこの処理が繰り返され、割り振られたエネルギーに基づいてピクセルの値(RGB)を重み付きで平均を行なうのです。
しかし、単純に2枚のレイヤしか無いときは、
p α + q (1−α) |
です。pは手前のレイヤのピクセル値。qは後ろのレイヤのピクセル値。αは手前のレイヤの不透明度(0〜100%) いま、αは0〜255の間の数字で欲しいので、この式は、
p α/255 + q (1−α/255) |
と変形できます。(αは0〜255ね)
そして、後ろの(透明な)レイヤを、白に塗った画像Aと、黒に塗った画像Bの2枚を用意します。このとき、qの(R,G,B)=(255,255,255),(0,0,0)ですね。つまり、上の式に代入すると、
Aの画像:p α + (255,255,255)(1−α/255) |
です。Aの画像からBの画像の差を取ると、(255−α,255−α,255−α)となり、RでもBでもGのいずれも255−αとなるので、255からRの値でも引いてやれば求めるべきαの値となります。
サクっとプログラムを書きましょう。
for(int y=0;y<sy;y++){ for(int x=0;x<sx;x++){ DWORD dw; dw = *p[0] & 0xffffff; // 最上位バイトをマスク BYTE alpha; alpha = 255 - ((*p[1]&0xff) - (*p[0]&0xff)); dw += ((DWORD)alpha) << 24; // 最上位バイトに持ってくる *(pd++) = dw; p[0]++; p[1]++; } } |
実に簡単です。あとはこのαチャンネル付きのデータにヘッダをつけて、実際のボディはyaneSDK2ndのCLZSSで圧縮すればαチャンネル付きの画像フォーマットが完成。Yaneurao Graphic format with Alpha channel略してYGAと命名。yaneSDK2ndのCDIB32では標準でこの画像を読めこめるようにしてあります。このあと、αチャンネル有効な転送処理をいくつか追加。
ただし、このα値の有効なブレンド転送は、0−255の間なので、さきほどの
p α/255 + q (1−α/255) |
で行なわなければならないのですが、この255で割る作業が、結構、ハイコストなのでビットシフトで済むように通常、256で割ります。そうすると、α=255にもかかわらず色が少し減衰するという現象が発生します。これを防ぐために、αが255のときは256に補整する処理を入れます。これでバッチリです。ちなみにヌキ色有効な転送は必要ありません。ヌキ色の部分はα=0にしておけば良いからです。
いま製作中のWAFFLEの『HAPPYほたる荘』で、これをテストしてもらったところ、非常に良好な結果が出たので、『HAPPYほたる荘』の立ちキャラ等は、すべてこの形式で行なうことにしました。
従来、画面のインターフェースはヌキ色の処理が非常に面倒で、グラフィッカーに負担を掛け、かつジャギとの格闘だったのですが、これで完全に解消できたことになります。
Leafの『猪名川で行こう!』にしても、何だかんだ言っても2階調のレイヤマスクしか持っていなかった(と思う)のに対し、αチャンネル付き転送を実装したのは、業界初でしょうか..?そんなわけで、『HAPPY ほたる荘』買ってくださいね^^;
'00/10/19 追加。↑業界初ではなかったらしい(笑) こんなのぐらい簡単なんだから誰かやってるって…。透明部分を白塗りと黒塗りしたものからα情報を抜き出すあたりは斬新だと思ったんだけどな〜。
'00/10/20 追加。PhotoShopからならばαチャンネルを作って、RAW形式で書き出せばαチャンネルも出せるようだ。参考になりそうなところは、ここかな?手間は、上の方法とそんなに変わらないような...
第CF回 これからはANTIALIASED_QUALITY(アンチェリ物語完結編^^;) 00/11/02
やねうらおは、いまだ半そでシャツ一枚なんですよ。それと言うのも長袖の服とかシャツとか持ってないんですよ。それというのも金が無いからですとも。ええ。会社のみんなからは、ハードディスクとかノーパソ買う金があるんなら、冬服を買え!と突っ込まれまくってますが、ハードディスクとかノーパソを買う金はあっても服を買う金は無いんですよ。わかりますか?わかりますよね?(笑)
それと言うのも、上京してくるときにこちらに冬服を持ってきていなかったからなんですが…。送ってもらおうと思って実家に電話しました。
「あんた、ええとこに電話してきてくれたわ」
なんや?ええとこやったんか?それは、邪魔して悪かったな。オレもテレビドラマ見ててええとこで電話かかってきたらムカっとくるもんな。ほなしゃいなら〜。
「違うがな。ちょっと、相談があんねや」
なんや、相談かいな…。わかったよ。何でも言うてよ。
「それというのも、儒珍トイレがあふれたんや」
えっ!そりゃ大変や。水びたしやな。詰まったんか?キュポキュポせな!
「ちがうがな。儒珍トイレがいっぱいなんや」
せやから、水びたしなんやろ?キュポキュポしたらええがな。
「水は出てへん〜」
なんや?その、儒珍トイレってのは、水洗式とは違うんか?ついに我が家も流行りのシャワートイレとかになったんかと思ったぞ。しかし、うちのトイレ、いつの間に儒珍式になってしもたんや?
「ちがうがな。パソコンで、メール入ってくるトイレがあるやろ?」
えっ?メール入ってくるトイレって何や...
「あれが、いっぱいなってしもたんや」
メール入ってくるトイレ、メール入ってくるトイレ・・儒珍トイレ、じゅちんトイレ、じゅしんトイレ・・・それは受信トレイやろ!!何が儒珍トイレなんや、はっきりしゃべれ!はっきり!しかも、トイレじゃなくて、ト・レ・イ。たちつてとのトに、らりるれろのレ、あいうえおのイで、ト・レ・イ。受信ト・レ・イ。おわかり?
「そうとも言う」
そうとしか言わへんちゅーの!
そんなやりとりのあと、冬服を送ってくれるように頼むと…
「ああ?冬服?間違って捨てたわ。ごめんごめん」
捨てたって…何をどう間違ったら捨てられるかな?
「夏にあんたの部屋、掃除してたんや。そしたら、あんたの冬服出てきてな、いま夏やし、これいらんやろとか思って、捨てたわ。ごめんごめん」
ごめんごめんって…(笑) そりゃ夏に冬服はいらんやろけど、捨てたって、どういうこと?もう無いんですか?
「また買ったらええやん。たんと〜儲けてんねやろ?」
そりゃ、服ぐらい買えるけどやなぁ、馬車馬のように働いてやっと手に入れた金やで!毎年勝手に服、捨てられたんではたまらんがな!
「服はよ買わんと風邪ひくで」
誰が捨てたんじゃい!誰が。まさか捨てたとは思わんかったわい。
「儒珍トイレの件、ありがとーな」
だから、儒珍トイレと違うゆーてるやろ…。
うちの母親は、かなり困った人である。たぶん頭のネジが16本ぐらい足りないのだろう。
さてさて。今回は、文字フォントのクオリティの話です。::CreateFontでフォントを作成するときに指定できるのは、DEFAULT_QUALITY(ディフォルト)とDRAFT_QUALITY(それなり)、PROOF_QUALITY(ばっちぐー^^;)の3つだと思われていますが、それは違うのです。実は、隠しオプションで、NONANTIALIASED_QUALITY(アンチェリなし)とANTIALIASED_QUALITY(アンチェリ有り)というのがあるのです。
どうも、Windows95+MicrosoftPlus!の入っている環境で動くらしいのですが(Windows98以降ならば、Plus!なしで使える)、こいつがまた困ったやつなのです。そもそも、私は自分のWindows2000マシンでPROOF_QUALITYでテストしていたのですが、ルーペで拡大して見たら勝手にアンチェリがかけてあるのです。んなアホな…と思い、別のWindows2000マシンでテストしたところ、そちらではアンチェリがかからないわけです。
調べてみると画面のプロパティ→効果→「スクリーンフォントの縁を滑らかにする」をONにしていると、勝手にアンチェリをかけるようなのです。はっきり言って余計なお世話で、勝手にアンチェリをかけられると、アンチェリ部分はそのときの背景色(yaneSDK2ndのCDIB,CPlaneでは黒)とブレンドした色になるので、それをどこかに描画しようとすると黒が混じってくるのです。
アンチェリなしにするには、「PROOF_QUALITY | NONANTIALIASED_QUALITY」とすればよさそうなものですが、これではうまくいかないのです。なんでかな〜と思って、WinGDI.hを見てビックリ仰天!(死語)
WINGDI.H(1185): #define DEFAULT_QUALITY 0 WINGDI.H(1186): #define DRAFT_QUALITY 1 WINGDI.H(1187): #define PROOF_QUALITY 2 WINGDI.H(1189): #define NONANTIALIASED_QUALITY 3 WINGDI.H(1190): #define ANTIALIASED_QUALITY 4 |
うひゃー。やってくれました(笑) 見ての通り、併用不可なのです。ちゅーことは、たとえばNONANTIALIASED_QUALITYを指定したとき、クオリティは、PROOFなんか、DRAFTなんか、どれやねん!?って思うわけです。困った奴です。そもそも、PROOFとDRAFTの差なんか画面で見てもわかりません。本当に変わってるの!? 謎は深まるばかりです。
考えても無駄っぽいので、とりあえず、アンチェリして欲しいときはANTIALIASED_QUALITY、アンチェリされると困るときは、NONANTIALIASED_QUALITYを指定するしかなさそうです。アンチェリしたかったら、すれば〜?ってときだけPROOF_QUALITYかDRAFT_QUALITYか、DEFAULT_QUALITYを選ぶという使いわけになりそうです。そんなことは、オンラインヘルプに書いとけ!って感じです。
で、さらに実験してるとANTIALIASED_QUALITYを指定しているのにアンチェリがかからない状況が発生。どうも調べてみると、フォントサイズが24以下だとアンチェリがかからないようです。しかも他の環境で試すと、25以上でもアンチェリがかからないときたもんだ。うーん。困ったやつだなぁ…。
こうなってくると、NONANTIALIASED_QUALITYで描画して自前でアンチェリをかけるしか無さそうです。とりあえずぼかし(yaneSDK2ndのCDIB32::ShadeOff)をかけて、それをα値として転送してみたんですが…どうも下手くそ〜な文字に見えます(笑) だめだこりゃーってことで、正確に縦、横2倍のサイズで描いて、それを縮小するとき、すなわち2×2ドットが1つのドットになるわけで、その4つのドットの加重平均をα値にすればよさそうです。確かにこの方法はうまく行くのですが、正確に縦、横2倍サイズで描くのは不可能なのです。なぜならば、フォントサイズとして2倍を指定したからと言って、文字サイズが正確に2倍になるわけではないからです。まあ、2倍のフォントサイズを指定したときに得られるTextExtentPointの半分の大きさになると考えれば良いのかも知れませんが、アンチェリ文字のときだけ生成されるプレーンのサイズが違うのもまた気持ち悪いのです。
そのへんを適当にごまかしたのがyaneSDK2nd1.00β3のCTextDIB32::UpdateTextAです。動いてるのは、yaneSDK2ndのサンプル13がそれです。