BREW のアルファブレンド

大分前に id:enra さんが高速なアルファブレンドを行うための手法を書かれていた(id:enra:20050124)ので、それを見ながら実装してみました。


まずはテーブルの生成。

さて、この問題で高速化したいのは「5bit精度の整数値A,Bを5bit精度の固定小数値t*1で線形補間した時の値C」を求める部分です。それならばこの結果自体を一気に求められるようなテーブルを作ってしまいましょう。

とのことなので、

m_pTable = new byte[32 * 32 * 32]; // 線形補間テーブル生成
for (int a = 0; a < 32; a++)
    for (int s = 0; s < 32; s++)
        for (int d = 0; d < 32; d++)
            // src*alpha+dst*(31-alpha)
            m_pTable[(a << 10) | (s << 5) | d] = (s * a + d * (31 - a)) >> 5;

こうやってテーブルを生成します。
上位の 5bit をアルファ値、次の 5bit を転送元画像、下位 5bit を転送先画像の値として計算しています。
alpha, src, dst の順番はあまり気にしなくていいんですが、下位 5bit を src か dst にすると、AND 一回分とシフト一回分少なくて済むので、1,2クロックぐらい減らすことが出来ます。
src と dst のアルファ値が enra さんの式と逆になってますが、自分はアルファ値が 31 の時に全部転送されて、0 の時は何も転送されない方がわかりやすいのでそうしてます。


で、あとはこれを転送します。

// dst: RGB565
// src: 8bit index
// インデックスによる透過あり
void ColorKeyAlpha(
    word* dst,      // 転送先画像
    int dx,         // 転送先 X 座標
    int dy,         // 転送先 Y 座標
    int dp,         // 転送先 pitch
    byte* src,      // 転送元画像(8bit index)
    int sx,         // 転送元 X 座標
    int sy,         // 転送元 Y 座標
    int sp,         // 転送元 pitch
    int w,          // 転送元 width
    int h,          // 転送元 height
    word* pal,      // パレット
    uint32 key,     // 透過インデックス
    int alpha)      // アルファ値(0〜31)
{
    byte* tbl = m_pTable;
    if (tbl == null) return;
    dst = (word*)((byte*)dst + dx * sizeof(word) + dy * dp);
    src = (byte*)((byte*)src + sx * sizeof(byte) + sy * sp);

    alpha <<= 10;

    for (int y = 0; y < h; y++)
    {
        for (int x = 0; x < w; x++)
        {
            if (src[x] != key)
            {
                word& s = *(word*)&pal[src[x]];
                word& d = *(word*)&dst[x];
                d = (tbl[alpha | ((s >>  6) & 0x03e0) | ((d >> 11)         )] << 11) | // R
                    (tbl[alpha | ((s >>  1) & 0x03e0) | ((d >>  6) & 0x001f)] <<  6) | // G
                    (tbl[alpha | ((s <<  5) & 0x03e0) | ((d      ) & 0x001f)] <<  0);  // B
            }
        }
        dst = (word*)((byte*)dst + dp);
        src = (byte*)((byte*)src + sp);
    }
}

R, G, B それぞれに対して線形補間をして、それを合成して dst に転送してます。
さすがにこれを 8 ループ展開とかアセンブラとかで書いても大して速度向上が望めそうにないのでこのまま。
以上アルファ転送でした(`・ω・´)


やっぱり画像に変形を加えないルーチンは楽だなぁ……。