IImage::copyToWithAlpha has a new parameter to allow combining alpha value instead of replacing them.

This uses new blitters called BLITTER_TEXTURE_COMBINE_ALPHA. 
Thx @chronologicaldot for providing this patch and @burningreggae for his feedback.


git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@5590 dfc29bdd-3216-0410-991c-e03cc46cb475
master
cutealien 2017-12-06 20:54:57 +00:00
parent c0f0b50915
commit c4247febe5
6 changed files with 253 additions and 9 deletions

View File

@ -1,5 +1,6 @@
--------------------------
Changes in 1.9 (not yet released)
- IImage::copyToWithAlpha has a new parameter to allow combining alpha value instead of replacing them. This uses new blitters called BLITTER_TEXTURE_COMBINE_ALPHA. Thx @chronologicaldot for providing this patch and @burningreggae for his feedback.
- Add _IRR_COMPILE_WITH_PARTICLES_ to control compilation of particle system
- Add IGUIImage::setDrawBackground to allow disabling background drawing even when no texture is set.
- Fix: IGUIContextMenu now raises sub-menu when they would otherwise be displayed below bottom-border of root gui element.

View File

@ -269,9 +269,12 @@ public:
virtual void copyTo(IImage* target, const core::position2d<s32>& pos, const core::rect<s32>& sourceRect, const core::rect<s32>* clipRect=0) =0;
//! copies this surface into another, using the alpha mask and cliprect and a color to add with
/** \param combineAlpha - When true then combine alpha channels. When false replace target image alpha with source image alpha.
*/
virtual void copyToWithAlpha(IImage* target, const core::position2d<s32>& pos,
const core::rect<s32>& sourceRect, const SColor &color,
const core::rect<s32>* clipRect = 0) =0;
const core::rect<s32>* clipRect = 0,
bool combineAlpha=false) =0;
//! copies this surface into another, scaling it to fit, applying a box filter
virtual void copyToScalingBoxFilter(IImage* target, s32 bias = 0, bool blend = false) = 0;

View File

@ -981,6 +981,166 @@ static void executeBlit_ColorAlpha_32_to_32( const SBlitJob * job )
}
}
/*!
Combine alpha channels (increases alpha / reduces transparency)
*/
static void executeBlit_TextureCombineColor_16_to_16( const SBlitJob * job )
{
const u32 w = job->width * 2;
const u32 h = job->height * 2;
u8* src = (u8*) job->src;
u8* dst = (u8*) job->dst;
const u16 jobColor = video::A8R8G8B8toA1R5G5B5( job->argb );
/*
Stretch not supported.
*/
for ( u32 dy = 0; dy != h; dy+=2 )
{
for ( u32 dx = 0; dx != w; dx+=2 )
{
const u16 src_x = src[dx] << 8 | src[dx+1];
const u16 dst_x = dst[dx] << 8 | dst[dx+1];
dst[dx] = PixelCombine16( dst_x, PixelMul16_2( src_x, jobColor ) );
}
src = (src) + job->srcPitch;
dst = (dst) + job->dstPitch;
}
}
/*!
Combine alpha channels (increases alpha / reduces transparency)
*/
static void executeBlit_TextureCombineColor_16_to_24( const SBlitJob * job )
{
const u32 w = job->width;
const u32 h = job->height;
const u16 *src = static_cast<const u16*>(job->src);
u8 *dst = static_cast<u8*>(job->dst);
const u16 jobColor = video::A8R8G8B8toA1R5G5B5( job->argb );
if (job->stretch)
{
const float wscale = 1.f/job->x_stretch;
const float hscale = 1.f/job->y_stretch;
for ( u32 dy = 0; dy < h; ++dy )
{
const u32 src_y = (u32)(dy*hscale);
src = (u16*) ( (u8*) (job->src) + job->srcPitch*src_y );
for ( u32 dx = 0; dx < w; ++dx )
{
const u32 src_x = (u32)(dx*wscale);
u32 color = PixelMul16_2( video::A1R5G5B5toA8R8G8B8(src[src_x]), jobColor);
u8 * writeTo = &dst[dx * 3];
if ( video::getAlpha(src[src_x]) > 0 ) // only overlay if source has visible alpha (alpha == 1)
{
*writeTo++ = (color >> 16)& 0xFF;
*writeTo++ = (color >> 8) & 0xFF;
*writeTo++ = color & 0xFF;
}
}
dst += job->dstPitch;
}
}
else
{
for ( u32 dy = 0; dy != h; ++dy )
{
for ( u32 dx = 0; dx != w; ++dx )
{
u32 color = PixelMul16_2( video::A1R5G5B5toA8R8G8B8(src[dx]), jobColor);
u8 * writeTo = &dst[dx * 3];
if ( video::getAlpha(src[dx]) > 0 ) // only overlay if source has visible alpha (alpha == 1)
{
*writeTo++ = (color >> 16)& 0xFF;
*writeTo++ = (color >> 8) & 0xFF;
*writeTo++ = color & 0xFF;
}
}
src = (u16*) ( (u8*) (src) + job->srcPitch );
dst += job->dstPitch;
}
}
}
/*!
Combine alpha channels (increases alpha / reduces transparency)
Destination alpha is treated as full 255
*/
static void executeBlit_TextureCombineColor_32_to_24( const SBlitJob * job )
{
const u32 w = job->width;
const u32 h = job->height;
const u32 *src = static_cast<const u32*>(job->src);
u8 *dst = static_cast<u8*>(job->dst);
if (job->stretch)
{
const float wscale = 1.f/job->x_stretch;
const float hscale = 1.f/job->y_stretch;
for ( u32 dy = 0; dy < h; ++dy )
{
const u32 src_y = (u32)(dy*hscale);
src = (u32*) ( (u8*) (job->src) + job->srcPitch*src_y);
for ( u32 dx = 0; dx < w; ++dx )
{
const u32 src_x = src[(u32)(dx*wscale)];
u8* writeTo = &dst[dx * 3];
const u32 dst_x = 0xFF000000 | writeTo[0] << 16 | writeTo[1] << 8 | writeTo[2];
const u32 combo = PixelCombine32( dst_x, PixelMul32_2( src_x, job->argb ) );
*writeTo++ = (combo >> 16) & 0xFF;
*writeTo++ = (combo >> 8) & 0xFF;
*writeTo++ = combo & 0xFF;
}
dst += job->dstPitch;
}
}
else
{
for ( u32 dy = 0; dy != h; ++dy )
{
for ( u32 dx = 0; dx != w; ++dx )
{
u8* writeTo = &dst[dx * 3];
const u32 dst_x = 0xFF000000 | writeTo[0] << 16 | writeTo[1] << 8 | writeTo[2];
const u32 combo = PixelCombine32( dst_x, PixelMul32_2( src[dx], job->argb ) );
*writeTo++ = (combo >> 16) & 0xFF;
*writeTo++ = (combo >> 8) & 0xFF;
*writeTo++ = combo & 0xFF;
}
src = (u32*) ( (u8*) (src) + job->srcPitch );
dst += job->dstPitch;
}
}
}
/*!
Combine alpha channels (increases alpha / reduces transparency)
*/
static void executeBlit_TextureCombineColor_32_to_32( const SBlitJob * job )
{
u32 *src = (u32*) job->src;
u32 *dst = (u32*) job->dst;
for ( s32 dy = 0; dy != job->height; ++dy )
{
for ( s32 dx = 0; dx != job->width; ++dx )
{
dst[dx] = PixelCombine32( dst[dx], PixelMul32_2( src[dx], job->argb ) );
}
src = (u32*) ( (u8*) (src) + job->srcPitch );
dst = (u32*) ( (u8*) (dst) + job->dstPitch );
}
}
// Blitter Operation
enum eBlitter
{
@ -989,7 +1149,8 @@ enum eBlitter
BLITTER_COLOR_ALPHA,
BLITTER_TEXTURE,
BLITTER_TEXTURE_ALPHA_BLEND,
BLITTER_TEXTURE_ALPHA_COLOR_BLEND
BLITTER_TEXTURE_ALPHA_COLOR_BLEND,
BLITTER_TEXTURE_COMBINE_ALPHA,
};
typedef void (*tExecuteBlit) ( const SBlitJob * job );
@ -1022,6 +1183,14 @@ static const blitterTable blitTable[] =
{ BLITTER_COLOR, video::ECF_A8R8G8B8, -1, executeBlit_Color_32_to_32 },
{ BLITTER_COLOR_ALPHA, video::ECF_A1R5G5B5, -1, executeBlit_ColorAlpha_16_to_16 },
{ BLITTER_COLOR_ALPHA, video::ECF_A8R8G8B8, -1, executeBlit_ColorAlpha_32_to_32 },
{ BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_A8R8G8B8, video::ECF_A8R8G8B8, executeBlit_TextureCombineColor_32_to_32 },
{ BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_A8R8G8B8, video::ECF_R8G8B8, executeBlit_TextureCopy_24_to_32 },
{ BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_R8G8B8, video::ECF_A8R8G8B8, executeBlit_TextureCombineColor_32_to_24 },
{ BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_R8G8B8, video::ECF_R8G8B8, executeBlit_TextureCopy_x_to_x },
{ BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_A1R5G5B5, video::ECF_R8G8B8, executeBlit_TextureCopy_24_to_16 },
{ BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_A1R5G5B5, video::ECF_A1R5G5B5, executeBlit_TextureCombineColor_16_to_16 },
{ BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_A1R5G5B5, video::ECF_R8G8B8, executeBlit_TextureCopy_24_to_16 },
{ BLITTER_TEXTURE_COMBINE_ALPHA, video::ECF_R8G8B8, video::ECF_A1R5G5B5, executeBlit_TextureCombineColor_16_to_24 },
{ BLITTER_INVALID, -1, -1, 0 }
};

View File

@ -149,7 +149,7 @@ void CImage::copyTo(IImage* target, const core::position2d<s32>& pos, const core
//! copies this surface into another, using the alpha mask, a cliprect and a color to add with
void CImage::copyToWithAlpha(IImage* target, const core::position2d<s32>& pos, const core::rect<s32>& sourceRect, const SColor &color, const core::rect<s32>* clipRect)
void CImage::copyToWithAlpha(IImage* target, const core::position2d<s32>& pos, const core::rect<s32>& sourceRect, const SColor &color, const core::rect<s32>* clipRect, bool combineAlpha)
{
if (IImage::isCompressedFormat(Format))
{
@ -157,9 +157,16 @@ void CImage::copyToWithAlpha(IImage* target, const core::position2d<s32>& pos, c
return;
}
// color blend only necessary on not full spectrum aka. color.color != 0xFFFFFFFF
Blit(color.color == 0xFFFFFFFF ? BLITTER_TEXTURE_ALPHA_BLEND: BLITTER_TEXTURE_ALPHA_COLOR_BLEND,
target, clipRect, &pos, this, &sourceRect, color.color);
if ( combineAlpha )
{
Blit(BLITTER_TEXTURE_COMBINE_ALPHA, target, clipRect, &pos, this, &sourceRect, color.color);
}
else
{
// color blend only necessary on not full spectrum aka. color.color != 0xFFFFFFFF
Blit(color.color == 0xFFFFFFFF ? BLITTER_TEXTURE_ALPHA_BLEND: BLITTER_TEXTURE_ALPHA_COLOR_BLEND,
target, clipRect, &pos, this, &sourceRect, color.color);
}
}

View File

@ -50,7 +50,7 @@ public:
//! copies this surface into another, using the alpha mask, an cliprect and a color to add with
virtual void copyToWithAlpha(IImage* target, const core::position2d<s32>& pos,
const core::rect<s32>& sourceRect, const SColor &color,
const core::rect<s32>* clipRect = 0) _IRR_OVERRIDE_;
const core::rect<s32>* clipRect = 0, bool combineAlpha=false) _IRR_OVERRIDE_;
//! copies this surface into another, scaling it to fit, applying a box filter
virtual void copyToScalingBoxFilter(IImage* target, s32 bias = 0, bool blend = false) _IRR_OVERRIDE_;

View File

@ -342,7 +342,7 @@ inline u32 PixelBlend16_simd ( const u32 c2, const u32 c1 )
#endif
/*!
Pixel = dest * ( 1 - SourceAlpha ) + source * SourceAlpha
Pixel = dest * ( 1 - SourceAlpha ) + source * SourceAlpha (OpenGL blending)
*/
inline u32 PixelBlend32 ( const u32 c2, const u32 c1 )
{
@ -351,7 +351,6 @@ inline u32 PixelBlend32 ( const u32 c2, const u32 c1 )
if ( 0 == alpha )
return c2;
if ( 0xFF000000 == alpha )
{
return c1;
@ -386,6 +385,71 @@ inline u32 PixelBlend32 ( const u32 c2, const u32 c1 )
return (c1 & 0xFF000000) | rb | xg;
}
/*!
Pixel =>
color = sourceAlpha > 0 ? source, else dest
alpha = max(destAlpha, sourceAlpha)
*/
inline u16 PixelCombine16 ( const u16 c2, const u16 c1 )
{
if ( video::getAlpha(c1) > 0 )
return c1;
else
return c2;
}
/*!
Pixel =>
color = dest * ( 1 - SourceAlpha ) + source * SourceAlpha,
alpha = destAlpha * ( 1 - SourceAlpha ) + sourceAlpha
where "1" means "full scale" (255)
*/
inline u32 PixelCombine32 ( const u32 c2, const u32 c1 )
{
// alpha test
u32 alpha = c1 & 0xFF000000;
if ( 0 == alpha )
return c2;
if ( 0xFF000000 == alpha )
{
return c1;
}
alpha >>= 24;
// add highbit alpha, if ( alpha > 127 ) alpha += 1;
// stretches [0;255] to [0;256] to avoid division by 255. use division 256 == shr 8
alpha += ( alpha >> 7);
u32 srcRB = c1 & 0x00FF00FF;
u32 srcXG = c1 & 0x0000FF00;
u32 dstRB = c2 & 0x00FF00FF;
u32 dstXG = c2 & 0x0000FF00;
u32 rb = srcRB - dstRB;
u32 xg = srcXG - dstXG;
rb *= alpha;
xg *= alpha;
rb >>= 8;
xg >>= 8;
rb += dstRB;
xg += dstXG;
rb &= 0x00FF00FF;
xg &= 0x0000FF00;
u32 sa = c1 >> 24;
u32 da = c2 >> 24;
u32 blendAlpha_fix8 = (sa*256 + da*(256-alpha))>>8;
return blendAlpha_fix8 << 24 | rb | xg;
}
// ------------------ Fix Point ----------------------------------