Created
August 26, 2013 04:41
-
-
Save FrankHB/6338155 to your computer and use it in GitHub Desktop.
Alpha Compositing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
template<typename _type, std::uintmax_t _vNum = 1, std::uintmax_t _vDen = 1, | |
bool _bIsFloat = std::is_floating_point<_type>::value> | |
struct normalized_max; | |
template<typename _type, std::uintmax_t _vNum, std::uintmax_t _vDen> | |
struct normalized_max<_type, _vNum, _vDen, true> | |
{ | |
static yconstexpr _type value = double(_vNum / _vDen); | |
}; | |
template<typename _type, std::uintmax_t _vNum, std::uintmax_t _vDen> | |
struct normalized_max<_type, _vNum, _vDen, false> | |
{ | |
static yconstexpr _type value = std::numeric_limits<_type>::max(); | |
}; | |
/*! | |
\brief 像素迭代器透明操作。 | |
\warning 不检查迭代器有效性。 | |
\since build 437 | |
*/ | |
//@{ | |
//! \note 使用源迭代器对应像素的第 15 位表示透明性。 | |
template<typename _tOut, typename _tIn> | |
inline void | |
BlitTransparentPixel(_tOut& dst_iter, _tIn& src_iter) | |
{ | |
if(FetchAlpha(*src_iter)) | |
*dst_iter = *src_iter; | |
} | |
//! \note 使用 Alpha 通道表示透明性。 | |
template<typename _tOut> | |
inline void | |
BlitTransparentPixel(_tOut& dst_iter, IteratorPair& src_iter) | |
{ | |
*dst_iter = *src_iter.base().second & 0x80 ? FetchOpaque(*src_iter) | |
: FetchOpaque(PixelType()); | |
} | |
//@} | |
/*! | |
\ingroup BlitScanner | |
\brief 扫描线:按指定扫描顺序复制一行透明像素。 | |
\warning 不检查迭代器有效性。 | |
\since build 437 | |
*/ | |
template<bool _bPositiveScan> | |
struct BlitTransparentLine | |
{ | |
//! \since build 438 | |
template<typename _tOut, typename _tIn> | |
void | |
operator()(_tOut& dst_iter, _tIn& src_iter, SDst delta_x) | |
{ | |
for(SDst x(0); x < delta_x; ++x) | |
{ | |
BlitTransparentPixel(dst_iter, src_iter); | |
++src_iter; | |
ystdex::xcrease<_bPositiveScan>(dst_iter); | |
} | |
} | |
}; | |
/*! | |
\brief 像素组合器。 | |
\tparam _vDstAlphaBits 目标和结果 Alpha 位。 | |
\tparam _vSrcAlphaBits 源 Alpha 位。 | |
\pre 源和目标 Alpha (若存在)为归一化值(可以是浮点数或定点数)。 | |
\since build 439 | |
*/ | |
//@{ | |
template<size_t _vDstAlphaBits, size_t _vSrcAlphaBits> | |
struct GPixelCompositor | |
{ | |
/*! | |
\brief Alpha 组合 Alpha 分量。 | |
a := 1 - (1 - sa) * (1 - da) | |
= 1 - (1 - sa - da + sa * da) | |
= sa + da - sa * da | |
= sa + da * (1 - sa) | |
*/ | |
template<typename _tDstAlpha, typename _tSrcAlpha> | |
static yconstexpr _tDstAlpha | |
CompositeAlphaOver(_tDstAlpha da, _tSrcAlpha sa) | |
{ | |
return sa + da * (1 - sa); | |
} | |
/*! | |
\brief Alpha 组合非 Alpha 分量。 | |
a != 0 | |
=> c := (sa * s + (1 - sa) * da * a) / a | |
= (sa * s + (a - sa) * d) / a | |
= (sa * s + a * d - sa * d) / a | |
= (sa * (s - d) + a * d) / a | |
= sa * (s - d) / a + d | |
*/ | |
template<typename _tDst, typename _tSrc, typename _tSrcAlpha, | |
typename _tAlpha> | |
static yconstexpr auto | |
CompositeComponentOver(_tDst d, _tSrc s, _tSrcAlpha sa, _tAlpha a) | |
-> decltype(a == 0 ? sa * (s - d) / a + d : 0) | |
{ | |
return a != 0 ? sa * (s - d) / a + d : 0; | |
} | |
}; | |
//! \note 1 位源 Alpha 。 | |
template<size_t _vDstAlphaBits> | |
struct GPixelCompositor<_vDstAlphaBits, 1> | |
{ | |
/*! | |
\brief Alpha 组合 Alpha 分量。 | |
a := sa + da * (1 - sa) | |
= sa != 0 ? 1 : da | |
*/ | |
template<typename _tDstAlpha, typename _tSrcAlpha> | |
static yconstexpr _tDstAlpha | |
CompositeAlphaOver(_tDstAlpha da, _tSrcAlpha sa) | |
{ | |
return sa != 0 ? ystdex::normalized_max<_tSrcAlpha>::value : da; | |
} | |
/*! | |
\brief Alpha 组合非 Alpha 分量。 | |
a != 0 | |
=> c := sa * (s - d) / a + d | |
= sa != 0 ? (s - d) / a + d : d | |
*/ | |
template<typename _tDst, typename _tSrc, typename _tSrcAlpha, | |
typename _tAlpha> | |
static yconstexpr auto | |
CompositeComponentOver(_tDst d, _tSrc s, _tSrcAlpha sa, _tAlpha a) | |
-> decltype(a != 0 ? (sa ? (s - d) / a + d : d) : 0) | |
{ | |
return a != 0 ? (sa != 0 ? (s - d) / a + d : d) : 0; | |
} | |
}; | |
//! \note 不透明源。 | |
template<size_t _vDstAlphaBits> | |
struct GPixelCompositor<_vDstAlphaBits, 0> | |
{ | |
/*! | |
\brief Alpha 组合 Alpha 分量。 | |
sa = 1 | |
=> a := sa + da * (1 - sa) | |
= 1 | |
*/ | |
template<typename _tDstAlpha> | |
static yconstexpr _tDstAlpha | |
CompositeAlphaOver(_tDstAlpha da) | |
{ | |
return ystdex::normalized_max<_tDstAlpha>::value; | |
} | |
template<typename _tDstAlpha, typename _tSrcAlpha> | |
static yconstexpr _tDstAlpha | |
CompositeAlphaOver(_tDstAlpha da, _tSrcAlpha) | |
{ | |
return ystdex::normalized_max<_tDstAlpha>::value; | |
} | |
/*! | |
\brief Alpha 组合非 Alpha 分量。 | |
sa := 1 | |
=> a := 1 | |
=> c := sa * (s - d) / a + d | |
= s - d + d | |
= s | |
*/ | |
template<typename _tSrc> | |
static yconstexpr _tSrc | |
CompositeComponentOver(_tSrc s) | |
{ | |
return s; | |
} | |
template<typename _tDst, typename _tSrc, typename _tSrcAlpha, | |
typename _tAlpha> | |
static yconstexpr _tSrc | |
CompositeComponentOver(_tDst, _tSrc s, _tSrcAlpha, _tAlpha) | |
{ | |
return s; | |
} | |
}; | |
//! \note 1 位目标 Alpha 。 | |
template<size_t _vSrcAlphaBits> | |
struct GPixelCompositor<1, _vSrcAlphaBits> | |
{ | |
/*! | |
\brief Alpha 组合 Alpha 分量。 | |
a := sa + da * (1 - sa) | |
= da != 0 ? 1 : sa | |
*/ | |
template<typename _tDstAlpha, typename _tSrcAlpha> | |
static yconstexpr _tDstAlpha | |
CompositeAlphaOver(_tDstAlpha da, _tSrcAlpha sa) | |
{ | |
return da != 0 ? ystdex::normalized_max<_tDstAlpha>::value : sa; | |
} | |
/*! | |
\brief Alpha 组合非 Alpha 分量。 | |
a != 0 | |
=> c := sa * (s - d) / a + d | |
= sa * (s - d) + d | |
*/ | |
template<typename _tDst, typename _tSrc, typename _tSrcAlpha, | |
typename _tAlpha> | |
static yconstexpr auto | |
CompositeComponentOver(_tDst d, _tSrc s, _tSrcAlpha sa, _tAlpha a) | |
-> decltype(a != 0 ? (sa ? (s - d) / a + d : d) : 0) | |
{ | |
return a != 0 ? (sa * (s - d) + d) : 0; | |
} | |
}; | |
//! \note 不透明目标。 | |
template<size_t _vSrcAlphaBits> | |
struct GPixelCompositor<0, _vSrcAlphaBits> | |
{ | |
/*! | |
\brief Alpha 组合 Alpha 分量。 | |
da = 1 | |
=> a := sa + da * (1 - sa) | |
= 1 | |
*/ | |
template<typename _tDstAlpha> | |
static yconstexpr _tDstAlpha | |
CompositeAlphaOver(_tDstAlpha da) | |
{ | |
return ystdex::normalized_max<_tDstAlpha>::value; | |
} | |
template<typename _tDstAlpha, typename _tSrcAlpha> | |
static yconstexpr _tDstAlpha | |
CompositeAlphaOver(_tDstAlpha da, _tSrcAlpha) | |
{ | |
return ystdex::normalized_max<_tDstAlpha>::value; | |
} | |
/*! | |
\brief Alpha 组合非 Alpha 分量。 | |
da = 1 | |
=> a := 1 | |
=> c := sa * (s - d) / a + d | |
= sa * (s - d) + d | |
*/ | |
template<typename _tDst, typename _tSrc, typename _tSrcAlpha> | |
static yconstexpr auto | |
BlendComponent(_tDst d, _tSrc s, _tSrcAlpha sa) | |
-> decltype(sa * (s - d) + d) | |
{ | |
return sa * (s - d) + d; | |
} | |
template<typename _tDst, typename _tSrc, typename _tSrcAlpha, | |
typename _tAlpha> | |
static yconstexpr auto | |
BlendComponent(_tDst d, _tSrc s, _tSrcAlpha sa, _tAlpha) | |
-> decltype(BlendComponent(d, s, sa)) | |
{ | |
return BlendComponent(d, s, sa); | |
} | |
}; | |
//! \note 1 位源和目标 Alpha 。 | |
template<> | |
struct GPixelCompositor<1, 1> | |
{ | |
/*! | |
\brief Alpha 组合 Alpha 分量。 | |
a := sa + da * (1 - sa) | |
= sa != 0 || da != 0 | |
*/ | |
template<typename _tDstAlpha, typename _tSrcAlpha> | |
static yconstexpr _tDstAlpha | |
CompositeAlphaOver(_tDstAlpha da, _tSrcAlpha sa) | |
{ | |
return sa != 0 || da != 0 ? ystdex::normalized_max<_tDstAlpha>::value : 0; | |
} | |
/*! | |
\brief Alpha 组合非 Alpha 分量。 | |
a != 0 | |
=> c := sa * (s - d) / a + d | |
= sa * (s - d) + d | |
*/ | |
template<typename _tDst, typename _tSrc, typename _tSrcAlpha, | |
typename _tAlpha> | |
static yconstexpr auto | |
CompositeComponentOver_s1d1(_tDst d, _tSrc s, _tSrcAlpha sa, _tAlpha a) | |
-> decltype(a != 0 ? sa * (s - d) + d : 0) | |
{ | |
return a != 0 ? sa * (s - d) + d : 0; | |
} | |
}; | |
//! \note 不透明源和目标。 | |
template<> | |
struct GPixelCompositor<0, 0> | |
{ | |
/*! | |
\brief Alpha 组合 Alpha 分量。 | |
sa = 1 && da = 1 | |
=> a := sa + da * (1 - sa) | |
= 1 | |
*/ | |
template<typename _tDstAlpha> | |
static yconstexpr _tDstAlpha | |
CompositeAlphaOver(_tDstAlpha da) | |
{ | |
return ystdex::normalized_max<_tDstAlpha>::value; | |
} | |
template<typename _tDstAlpha, typename _tSrcAlpha> | |
static yconstexpr _tDstAlpha | |
CompositeAlphaOver(_tDstAlpha da, _tSrcAlpha) | |
{ | |
return ystdex::normalized_max<_tDstAlpha>::value; | |
} | |
/*! | |
\brief Alpha 组合非 Alpha 分量。 | |
sa == 1 && a == 1 | |
=> c := sa * (s - d) / a + d | |
= s - d + d | |
= s | |
*/ | |
template<typename _tSrc> | |
static yconstexpr _tSrc | |
CompositeComponentOver(_tSrc s) | |
{ | |
return s; | |
} | |
template<typename _tDst, typename _tSrc, typename _tSrcAlpha, | |
typename _tAlpha> | |
static yconstexpr _tDst | |
CompositeComponentOver(_tDst, _tSrc s, _tSrcAlpha, _tAlpha) | |
{ | |
return s; | |
} | |
}; | |
//@} | |
/*! | |
\brief 像素混合器。 | |
\tparam _tPixel 像素类型。 | |
\tparam _vAlphaBits Alpha 宽度。 | |
\since build 439 | |
\todo 支持浮点 Alpha 类型。 | |
*/ | |
//@{ | |
template<typename _tPixel, size_t _vAlphaBits> | |
struct GPixelBlender | |
{ | |
static yconstexpr size_t AlphaBits = _vAlphaBits; | |
static yconstexpr size_t MaxAlpha = (1 << AlphaBits) - 1; | |
static yconstexpr size_t BlitRound = 1 << (AlphaBits - 1); | |
/*! | |
\brief Alpha 分量混合。 | |
设归一化 Alpha 值 alpha = a / MaxAlpha ,输出结果: | |
result := (1 - alpha) * d + alpha * s | |
= ((MaxAlpha - a) * d + a * s) >> AlphaBits | |
= d + ((a * (s - d) + BlitRound) >> AlphaBits) 。 | |
*/ | |
template<typename _tMono, typename _tAlpha> | |
static yconstfn _tMono | |
BlendComponent(_tMono d, _tMono s, _tAlpha a) | |
{ | |
return d + ((a * (s - d) + BlitRound) >> AlphaBits); | |
} | |
/* | |
\brief Alpha 混合。 | |
使用下列公式进行像素的 Alpha 混合(其中 alpha = a / MaxAlpha): | |
背景透明, 输出 Alpha 饱和。 | |
*/ | |
template<typename _tAlpha> | |
static _tPixel | |
Blend(_tPixel d, _tPixel s, _tAlpha a) | |
{ | |
const Color dc(d), sc(s); | |
return Color(BlendComponent(dc.GetR(), sc.GetR(), a), | |
BlendComponent(dc.GetG(), sc.GetG(), a), | |
BlendComponent(dc.GetB(), sc.GetB(), a), MaxAlpha); | |
} | |
}; | |
template<size_t _vAlphaBits> | |
struct GPixelBlender<u16, _vAlphaBits> | |
{ | |
static yconstexpr size_t AlphaBits = _vAlphaBits; | |
static yconstexpr size_t MaxAlpha = (1 << AlphaBits) - 1; | |
static yconstexpr size_t BlitRound = 1 << (AlphaBits - 1); | |
static yconstexpr u32 BlitRound_XZ = BlitRound | BlitRound << 16; | |
template<typename _tAlpha> | |
static u16 | |
Blend(u16 d, u16 s, _tAlpha a) | |
{ | |
return BlendCore(d, s, a); | |
} | |
/* | |
\brief AXYZ1555 格式 PixelType 的 Alpha 混合。 | |
\since build 417 | |
使用下列公式进行像素的 Alpha 混合(其中 alpha = a / MaxAlpha): | |
输出分量: component := (1 - alpha) * d + alpha * s | |
= ((MaxAlpha - a) * d + a * s) >> AlphaBits | |
= d + ((a * (s - d) + BlitRound) >> AlphaBits) 。 | |
背景透明,输出 Alpha 饱和。 | |
像素格式: 16 位 AXYZ1555 。 | |
以 ARGB1555 为例,算法实现示意: | |
arrrrrgggggbbbbb | |
0000000000arrrrr gggggbbbbb000000 | |
0000000000011111 0000000000011111 | |
00000000000rrrrr 00000000000bbbbb : dbr | |
0000000000000000 000000ggggg00000 : dg | |
分解分量至 32 位寄存器以减少总指令数。 | |
*/ | |
static u16 | |
BlendCore(u32 d, u32 s, u8 a) | |
{ | |
u32 dbr((d & 0x1F) | (d << 6 & 0x1F0000)), dg(d & 0x3E0); | |
yunseq(dbr += ((((s & 0x1F) | (s << 6 & 0x1F0000)) - dbr) * a | |
+ BlitRound_XZ) >> AlphaBits, | |
dg += (((s & 0x3E0) - dg) * a + BlitRound) >> AlphaBits); | |
return (dbr & 0x1F) | (dg & 0x3E0) | (dbr >> 6 & 0x7C00) | 1 << 15; | |
} | |
}; | |
//@} | |
/*! | |
\brief 像素计算:Alpha 混合。 | |
\since build 438 | |
*/ | |
//@{ | |
template<typename _tOut> | |
inline void | |
BiltAlphaPoint(_tOut dst_iter, IteratorPair src_iter) | |
{ | |
static_assert(std::is_convertible<typename std::remove_reference< | |
decltype(*dst_iter)>::type, PixelType>::value, "Wrong type found."); | |
using Blender = GPixelBlender<PixelType, 8>; | |
const AlphaType a(*src_iter.base().second); | |
*dst_iter = Blender::Blend(*dst_iter, *src_iter, a); | |
} | |
template<typename _tOut, typename _tIn> | |
inline void | |
BiltAlphaPoint(_tOut dst_iter, ystdex::pair_iterator< | |
ystdex::pseudo_iterator<const PixelType>, _tIn> src_iter) | |
{ | |
static_assert(std::is_convertible<typename std::remove_reference< | |
decltype(*dst_iter)>::type, PixelType>::value, "Wrong type found."); | |
using Blender = GPixelBlender<PixelType, 8>; | |
const AlphaType a(*src_iter.base().second); | |
*dst_iter = Blender::Blend(*dst_iter, *src_iter, a); | |
} | |
//@} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment