Add an option to colorize to respect the destination alpha
authorSamuel Sieb <samuel@sieb.net>
Sun, 3 Apr 2016 06:35:42 +0000 (23:35 -0700)
committerkwolekr <kwolekr@minetest.net>
Sun, 3 Apr 2016 08:24:28 +0000 (04:24 -0400)
Also, rework the colorizing code to be more efficient.

doc/lua_api.txt
src/client/tile.cpp

index a01e728891b0a88a621cdec4c9d21bb12548ea88..4a4e345a926a014e619b97bc2a98efc241a6e15b 100644 (file)
@@ -357,8 +357,13 @@ The mask is applied using binary AND.
 #### `[colorize:<color>:<ratio>`
 Colorize the textures with the given color.
 `<color>` is specified as a `ColorString`.
-`<ratio>` is an int ranging from 0 to 255, and specifies how much of the
-color to apply. If ommitted, the alpha will be used.
+`<ratio>` is an int ranging from 0 to 255 or the word "`alpha`".  If
+it is an int, then it specifies how far to interpolate between the
+colors where 0 is only the texture color and 255 is only `<color>`. If
+omitted, the alpha of `<color>` will be used as the ratio.  If it is
+the word "`alpha`", then the alpha of the color will be multiplied with
+the alpha of the texture with the RGB of the color replacing the RGB of
+the texture.
 
 Sounds
 ------
index 7a9bc0159e4872b7cabf0508a4e8355123cbaed5..f0c0b4e9effb42ab9535d74a5d564cf4b67c996e 100644 (file)
@@ -558,6 +558,13 @@ static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst,
 static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst,
                v2s32 src_pos, v2s32 dst_pos, v2u32 size, int ratio);
 
+// Apply a color to an image.  Uses an int (0-255) to calculate the ratio.
+// If the ratio is 255 or -1 and keep_alpha is true, then it multiples the
+// color alpha with the destination alpha.
+// Otherwise, any pixels that are not fully transparent get the color alpha.
+static void apply_colorize(video::IImage *dst, v2s32 dst_pos, v2u32 size,
+               video::SColor color, int ratio, bool keep_alpha);
+
 // Apply a mask to an image
 static void apply_mask(video::IImage *mask, video::IImage *dst,
                v2s32 mask_pos, v2s32 dst_pos, v2u32 size);
@@ -1639,27 +1646,17 @@ bool TextureSource::generateImagePart(std::string part_of_name,
 
                        video::SColor color;
                        int ratio = -1;
+                       bool keep_alpha = false;
 
                        if (!parseColorString(color_str, color, false))
                                return false;
 
                        if (is_number(ratio_str))
                                ratio = mystoi(ratio_str, 0, 255);
+                       else if (ratio_str == "alpha")
+                               keep_alpha = true;
 
-                       core::dimension2d<u32> dim = baseimg->getDimension();
-                       video::IImage *img = driver->createImage(video::ECF_A8R8G8B8, dim);
-
-                       if (!img) {
-                               errorstream << "generateImagePart(): Could not create image "
-                                               << "for part_of_name=\"" << part_of_name
-                                               << "\", cancelling." << std::endl;
-                               return false;
-                       }
-
-                       img->fill(video::SColor(color));
-                       // Overlay the colored image
-                       blit_with_interpolate_overlay(img, baseimg, v2s32(0,0), v2s32(0,0), dim, ratio);
-                       img->drop();
+                       apply_colorize(baseimg, v2s32(0, 0), baseimg->getDimension(), color, ratio, keep_alpha);
                }
                else if (str_starts_with(part_of_name, "[applyfiltersformesh"))
                {
@@ -1783,6 +1780,44 @@ static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst
        }
 }
 
+/*
+       Apply color to destination
+*/
+static void apply_colorize(video::IImage *dst, v2s32 dst_pos, v2u32 size,
+               video::SColor color, int ratio, bool keep_alpha)
+{
+       u32 alpha = color.getAlpha();
+       video::SColor dst_c;
+       if ((ratio == -1 && alpha == 255) || ratio == 255) { // full replacement of color
+               if (keep_alpha) { // replace the color with alpha = dest alpha * color alpha
+                       dst_c = color;
+                       for (s32 y = dst_pos.Y; y < dst_pos.Y + size.Y; y++)
+                       for (s32 x = dst_pos.X; x < dst_pos.X + size.X; x++) {
+                               u32 dst_alpha = dst->getPixel(x, y).getAlpha();
+                               if (dst_alpha > 0) {
+                                       dst_c.setAlpha(dst_alpha * alpha / 255);
+                                       dst->setPixel(x, y, dst_c);
+                               }
+                       }
+               } else { // replace the color including the alpha
+                       for (s32 y = dst_pos.Y; y < dst_pos.Y + size.Y; y++)
+                       for (s32 x = dst_pos.X; x < dst_pos.X + size.X; x++)
+                               if (dst->getPixel(x, y).getAlpha() > 0)
+                                       dst->setPixel(x, y, color);
+               }
+       } else {  // interpolate between the color and destination
+               float interp = (ratio == -1 ? color.getAlpha() / 255.0f : ratio / 255.0f);
+               for (s32 y = dst_pos.Y; y < dst_pos.Y + size.Y; y++)
+               for (s32 x = dst_pos.X; x < dst_pos.X + size.X; x++) {
+                       dst_c = dst->getPixel(x, y);
+                       if (dst_c.getAlpha() > 0) {
+                               dst_c = color.getInterpolated(dst_c, interp);
+                               dst->setPixel(x, y, dst_c);
+                       }
+               }
+       }
+}
+
 /*
        Apply mask to destination
 */