Skip to content

Instantly share code, notes, and snippets.

@FrankHB
Created August 26, 2013 04:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FrankHB/6338155 to your computer and use it in GitHub Desktop.
Save FrankHB/6338155 to your computer and use it in GitHub Desktop.
Alpha Compositing
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