汎用高速アルファブレンド

id:melpon:20060920 で書いたコードの 256 段階バージョンを作る必要が出てきたんだけど、さすがに手で書くのはきついので、テンプレートを使ってちょこちょこ書いてみた。

//int GetDepth(int value)
//{
//  int count = 0;
//  while ((value & 1) == 0)
//  {
//      count++;
//      value >>= 1;
//  }
//  return count;
//}
//unsigned long AlphaBlendHelper(unsigned long dst, unsigned long src,
//                              int alpha, int maxBits, int nowAlpha)
//{
//  if (alpha == nowAlpha)
//  {
//      return detail::blend_half(dst, src);
//  }
//  else if (alpha < nowAlpha)
//  {
//      return AlphaBlendHelper(dst, detail::blend_half(dst, src),
//          alpha, maxBits, nowAlpha - (1 << (GetDepth(nowAlpha) - 1)));
//  }
//  else // if (alpha > nowAlpha)
//  {
//      return AlphaBlendHelper(detail::blend_half(dst, src), src,
//          alpha, maxBits, nowAlpha + (1 << (GetDepth(nowAlpha) - 1)));
//  }
//}
//
//unsigned long AlphaBlend(unsigned long dst, unsigned long src,
//                        int alpha, int maxBits)
//{
//  if (alpha <= 0)
//  {
//      return dst;
//  }
//  if (alpha >= 255)
//  {
//      return src;
//  }
//  return AlphaBlendHelper(dst, src, alpha, maxBits, 1 << (maxBits - 1));
//}

// 下記の内容は、上記の内容を template 化したもの。

namespace detail
{
    unsigned long blend_half(unsigned long dst, unsigned long src)
    {
        return (dst & src) + (((dst ^ src) & 0xfefefefe) >> 1);
    }

    template<int TValue>
    struct power2
    {
        static const int value = 1 << TValue;
    };

    template<int TValue, bool = (TValue & 1) == 0>
    struct depth
    {
        static const int value = 1 + depth<(TValue >> 1)>::value;
    };
    template<int TValue>
    struct depth<TValue, false>
    {
        static const int value = 0;
    };

    // TLeft < TRight の場合は -1
    // TLeft == TRight の場合は 0
    // TLeft > TRight の場合は 1
    template<int TLeft, int TRight>
    struct pred
    {
        static const int value = (TLeft == TRight) ? 0 : ((TLeft < TRight) ? -1 : 1);
    };

    template<int TAlpha, int TMaxAlphaBits, int TNowAlpha,
            int = pred<TAlpha, TNowAlpha>::value>
    struct alpha_blend_helper_internal
    {
        static unsigned long blend(unsigned long dst, unsigned long src)
        {
            return blend_half(dst, src);
        }
    };
    template<int TAlpha, int TMaxAlphaBits, int TNowAlpha>
    struct alpha_blend_helper_internal<TAlpha, TMaxAlphaBits, TNowAlpha, -1>
    {
        static unsigned long blend(unsigned long dst, unsigned long src)
        {
            return alpha_blend_helper_internal<TAlpha, TMaxAlphaBits,
                TNowAlpha - power2<depth<TNowAlpha>::value - 1>::value>::blend(
                dst, blend_half(dst, src));
        }
    };
    template<int TAlpha, int TMaxAlphaBits, int TNowAlpha>
    struct alpha_blend_helper_internal<TAlpha, TMaxAlphaBits, TNowAlpha, 1>
    {
        static unsigned long blend(unsigned long dst, unsigned long src)
        {
            return alpha_blend_helper_internal<TAlpha, TMaxAlphaBits,
                TNowAlpha + power2<depth<TNowAlpha>::value - 1>::value>::blend(
                blend_half(dst, src), src);
        }
    };

    // TLeft < TCenter < TRight の場合は 0
    // TLeft >= TCenter の場合は -1
    // TRight <= TCenter の場合は 1
    template<int TLeft, int TCenter, int TRight>
    struct pred2
    {
        static const int value = (TCenter <= TLeft) ? -1 : ((TCenter >= TRight) ? 1 : 0);
    };

    template<int TAlpha, int TMaxAlphaBits,
            int = pred2<0, TAlpha, power2<TMaxAlphaBits>::value>::value>
    struct alpha_blend_helper
    {
        static unsigned long blend(unsigned long dst, unsigned long src)
        {
            return alpha_blend_helper_internal<TAlpha, TMaxAlphaBits,
                power2<TMaxAlphaBits - 1>::value>::blend(dst, src);
        }
    };
    template<int TAlpha, int TMaxAlphaBits>
    struct alpha_blend_helper<TAlpha, TMaxAlphaBits, -1>
    {
        static unsigned long blend(unsigned long dst, unsigned long src)
        {
            return dst;
        }
    };
    template<int TAlpha, int TMaxAlphaBits>
    struct alpha_blend_helper<TAlpha, TMaxAlphaBits, 1>
    {
        static unsigned long blend(unsigned long dst, unsigned long src)
        {
            return src;
        }
    };
}

template<int TAlpha, int TMaxAlphaBits>
unsigned long AlphaBlend(unsigned long dst, unsigned long src)
{
    return detail::alpha_blend_helper<TAlpha, TMaxAlphaBits>::blend(dst, src);
}

で、ブレンドする際に必要なだけ実体化させる。

template<int TAlpha>
void BltInternal(Pixel32 dst, Pixel32 src, ...)
{
    for (int y = 0; y < ...)
    {
        for (int x = 0; x < ...)
        {
            dst[x] = AlphaBlend<TAlpha, 8>(dst[x], src[x]);
        }
        dst = dst.NextLine();
        src = src.NextLine();
    }
}

void Blt(Pixel32 dst, Pixel32, src, int alpha, ...)
{
    switch (alpha)
    {
    case   0: BltInternal<  0>(dst, src, ...); break;
    case   1: BltInternal<  1>(dst, src, ...); break;
    ...
    case 255: BltInternal<255>(dst, src, ...); break;
    case 256: BltInternal<256>(dst, src, ...); break;
    }
}

これでいけるはず。