Correction for alpha blending issues in texture mod compositing (#9029)
authorWarr1024 <warr1024@gmail.com>
Fri, 18 Oct 2019 17:17:35 +0000 (13:17 -0400)
committerSmallJoker <SmallJoker@users.noreply.github.com>
Fri, 18 Oct 2019 17:17:35 +0000 (19:17 +0200)
src/client/tile.cpp

index 3d9e2470a9cae8af9e04d19d9cb88d80f5882536..3189ab28c90e1182a072b9e2d147cd8427346ba6 100644 (file)
@@ -1811,6 +1811,24 @@ bool TextureSource::generateImagePart(std::string part_of_name,
        return true;
 }
 
+/*
+       Calculate the color of a single pixel drawn on top of another pixel.
+
+       This is a little more complicated than just video::SColor::getInterpolated
+       because getInterpolated does not handle alpha correctly.  For example, a
+       pixel with alpha=64 drawn atop a pixel with alpha=128 should yield a
+       pixel with alpha=160, while getInterpolated would yield alpha=96.
+*/
+static inline video::SColor blitPixel(const video::SColor &src_c, const video::SColor &dst_c, u32 ratio)
+{
+       if (dst_c.getAlpha() == 0)
+               return src_c;
+       video::SColor out_c = src_c.getInterpolated(dst_c, (float)ratio / 255.0f);
+       out_c.setAlpha(dst_c.getAlpha() + (255 - dst_c.getAlpha()) *
+               src_c.getAlpha() * ratio / (255 * 255));
+       return out_c;
+}
+
 /*
        Draw an image on top of an another one, using the alpha channel of the
        source image
@@ -1830,7 +1848,7 @@ static void blit_with_alpha(video::IImage *src, video::IImage *dst,
                s32 dst_y = dst_pos.Y + y0;
                video::SColor src_c = src->getPixel(src_x, src_y);
                video::SColor dst_c = dst->getPixel(dst_x, dst_y);
-               dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
+               dst_c = blitPixel(src_c, dst_c, src_c.getAlpha());
                dst->setPixel(dst_x, dst_y, dst_c);
        }
 }
@@ -1853,7 +1871,7 @@ static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
                video::SColor dst_c = dst->getPixel(dst_x, dst_y);
                if (dst_c.getAlpha() == 255 && src_c.getAlpha() != 0)
                {
-                       dst_c = src_c.getInterpolated(dst_c, (float)src_c.getAlpha()/255.0f);
+                       dst_c = blitPixel(src_c, dst_c, src_c.getAlpha());
                        dst->setPixel(dst_x, dst_y, dst_c);
                }
        }