When copying an image with alpha (the "source" image) to another image with alpha (the "destination" image), either via IImage::copyToWithAlpha() or via the Burnings Renderer (both of which call the same offending function), the source image's alpha channel completely replaces that of the destination alpha rather than blending the two. The offending code is in SoftwareDriver2_helper.h, function inline u32 PixelBlend32 ( const u32 c2, const u32 c1 ), line 347. Problem affects all current svn revisions up to 5589 (checked out today).
Code: Select all
/*!
Pixel = dest * ( 1 - SourceAlpha ) + source * SourceAlpha
*/
inline u32 PixelBlend32 ( 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;
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;
return (c1 & 0xFF000000) | rb | xg;
}
Again, the correct behavior should be blending the two. I propose the last line be replaced with the following fix:
Code: Select all
u32 alphaSum = (c1 >> 24) + (c2 >> 24);
alphaSum = (0x000000FF*((alphaSum & 0x0000FF00) > 0)) | (alphaSum & 0x000000FF);
return (alphaSum << 24) | rb | xg;
Code: Select all
u32 alphaSum = (c1 >> 24) + (c2 >> 24);
if ( alphaSum & 0x0000FF00 > 0 )
return 0xff000000 | rb | xg;
else
return ((alphaSum & 0x000000FF)<<24) | rb | xg;
I could send you an image, but it's probably easy enough for you to pick a couple random images from your computer and use this test:
Code: Select all
#include <irrlicht.h>
using namespace irr;
using namespace video;
int main() {
IrrlichtDevice* dev = createDevice(EDT_NULL);
IVideoDriver* vid = dev->getVideoDriver();
io::IFileSystem* fsys = dev->getFileSystem();
io::path myBackImagePath = ""; // set this to the path of your first image
io::path myFrontImagePath = ""; // set this to the path of your second image
IImage* backImage = vid->createImageFromFile(myBackImagePath);
IImage* frontImage = vid->createImageFromFile(myFrontImagePath);
if ( !backImage || !frontImage )
return 1;
video::SColor white(255,255,255,255);
core::vector2di zeroVector(0,0);
core::dimension2du frontImageSize = frontImage->getDimension();
core::dimension2di frontImageSizeI(frontImageSize);
core::recti frontImageSrcRect(zeroVector, core::vector2di(frontImageSizeI));
core::vector2di frontImageULC(0,0);
frontImage->copyToWithAlpha(backImage, frontImageULC, frontImageSrcRect, white);
vid->writeImageToFile(backImage, io::path("out.png"));
backImage->drop();
frontImage->drop();
return 0;
}
Side notes:
I tried rendering using OpenGL and it also gave unsatisfactory results. I'm not sure if Burnings is simply trying to mimic OpenGL activity or not, but it's rather annoying. I tried various values for the "white" color you see in my given example test as well as various render settings and materials before finding out it was this pixel blending function.