Entries

Flashのアルファブレンディングのイヤな仕様と回避策

FlashのBitmapData描画で、アルファ値の計算に色々問題があるのでメモ。

色1の上に色2をアルファありで描画した場合、新しい色は以下の式で求められる。

色 = int((色2-色1)*alpha + 色1)

Bitmap.DrawをはじめとするFlashの内部処理はどうやらこの式で求められている。だが、この式には大きな問題点がある。

実はこの式で、alpha = 0.01等の極めて薄い色をなんども重ねて描画すると、画面が徐々に暗くなったり、オレンジ等の中間色が赤や黄色にシフトするという奇妙な挙動が起きる。トップにある習作で、マウスを画面中央においていると、光がハレーションする部分にザラザラとしたノイズがかかるのはコレが原因だった。永らく原因不明だったが、ついに問題の根本をつきとめた。

これはブレンド計算した色がbitmapに渡されるとき、int型で丸められてしまう為だ。微妙な色は一律切り捨てられてしまう為、アルファの値によってはRGB特定の色だけ切り捨てが偏ったりしてしまう。つまり薄く色を重ねれば重ねるほど色がおかしくなっていく。

解決方法
この問題は、色をint型にキャストする前に、Math.roundで端数を四捨五入することで回避できる。

色 = Math.round((色2-色1)*alpha + 色1)

しかし高速化の為にピクセルを直接弄ってるのに、Math.roundを1ピクセル毎にやると負荷がかなりかかってしまう。そこで

var r = (r2-r1) * a + r1):
r = r * 2 + 1;
r = r >> 1;

といった具合に2倍して1足してから、ビットシフトで2割ることによって、Math.roundを使うことなしに四捨五入を行うことで回避できる。

1ピクセルごとに自力でまるめることでアルファブレンドの問題を解決できるわけだが、逆を言えばbitmap.drawではこの問題は解決できない。最終的にはバイトコードでピクセルを描画するのが、ベストということだろうか。メンドイね。