diff --git a/doc/function/index.txt b/doc/function/index.txt
index 7422aae4..b2050f88 100644
--- a/doc/function/index.txt
+++ b/doc/function/index.txt
@@ -83,7 +83,8 @@ These functions are built into the program, other [[type:function]]s can be defi
| [[fun:set_alpha]] Change the transparency of an image.
| [[fun:set_combine]] Change how the image should be combined with the background.
| [[fun:saturate]] Saturate/desaturate an image.
-| [[fun:invert]] Invert the colors of an image.
+| [[fun:invert_image]] Invert the colors of an image.
+| [[fun:recolor_image]] Change the colors of an image to match the font color.
| [[fun:enlarge]] Enlarge an image by putting a border around it.
| [[fun:crop]] Crop an image, giving only a small subset of it.
| [[fun:flip_horizontal]] Flip an image horizontally.
diff --git a/doc/function/invert.txt b/doc/function/invert.txt
deleted file mode 100644
index fdc0619f..00000000
--- a/doc/function/invert.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Function: invert
-
---Usage--
-> invert(input: image)
-
-Invert the colors in an image.
-
---Parameters--
-! Parameter Type Description
-| @input@ [[type:image]] Image to invert.
-
---Examples--
-> invert("image_logo.png") == [[Image]]
->>> invert(
) ==
diff --git a/doc/function/invert_image.txt b/doc/function/invert_image.txt
new file mode 100644
index 00000000..01bbeecf
--- /dev/null
+++ b/doc/function/invert_image.txt
@@ -0,0 +1,14 @@
+Function: invert_image
+
+--Usage--
+> invert_image(input: image)
+
+Invert the colors in an image.
+
+--Parameters--
+! Parameter Type Description
+| @input@ [[type:image]] Image to invert.
+
+--Examples--
+> invert_image("image_logo.png") == [[Image]]
+>>> invert_image(
) ==
diff --git a/doc/function/recolor_image.txt b/doc/function/recolor_image.txt
new file mode 100644
index 00000000..7b12ba02
--- /dev/null
+++ b/doc/function/recolor_image.txt
@@ -0,0 +1,26 @@
+Function: recolor_image
+
+--Usage--
+> recolor_image(input: image, color: color)
+
+Re-color an image:
+ * Red is replaced by the color
+ * Green is replaced by black or white, of the same lightness as the color.
+ So if the color is light, green will be replaced by white.
+ * Blue is replaced by black or white, of the opposite lightness.
+ * White stays white, black stays black
+
+This function is mostly intended to make symbols in a symbol font wich can match the text color.
+
+
+
+--Parameters--
+! Parameter Type Description
+| @input@ [[type:image]] Image to recolor.
+| @color@ [[type:color]] Color by which to replace red.
+
+--Examples--
+> recolor_image("symbol1.png", color: rgb(180,0,0)) == [[Image]]
+>>> recolor_image(
, color: rgb(180,0,0)) ==
+> recolor_image("symbol1.png", color: rgb(100,255,0)) == [[Image]]
+>>> recolor_image(
, color: rgb(100,255,0)) ==
diff --git a/src/data/symbol_font.cpp b/src/data/symbol_font.cpp
index 2d9ce390..c564a4b9 100644
--- a/src/data/symbol_font.cpp
+++ b/src/data/symbol_font.cpp
@@ -159,7 +159,10 @@ RealSize SymbolInFont::size(Package& pkg, double size) {
}
void SymbolInFont::update(Context& ctx) {
- image.update(ctx);
+ if (image.update(ctx)) {
+ // image has changed, cache is no longer valid
+ bitmaps.clear();
+ }
enabled.update(ctx);
if (text_font)
text_font->update(ctx);
diff --git a/src/gfx/color.cpp b/src/gfx/color.cpp
index ea53f749..4d3285b0 100644
--- a/src/gfx/color.cpp
+++ b/src/gfx/color.cpp
@@ -107,19 +107,16 @@ Color saturate(const Color& c, double amount) {
}
-void fill_image(Image& image, const Color& color) {
- Byte* pos = image.GetData();
- Byte* end = pos + image.GetWidth() * image.GetHeight() * 3;
- Byte r = color.Red(), g = color.Green(), b = color.Blue();
- if (r == g && r == b) {
+void fill_image(Image& image, RGB x) {
+ RGB* pos = (RGB*)image.GetData();
+ RGB* end = pos + image.GetWidth() * image.GetHeight();
+ if (x.r == x.g && x.r == x.b) {
// optimization: use memset
- memset(pos, r, end-pos);
+ memset(pos, x.r, (end-pos) * sizeof(*pos));
} else {
// fill the image
while (pos != end) {
- *pos++ = r;
- *pos++ = g;
- *pos++ = b;
+ *pos++ = x;
}
}
}
diff --git a/src/gfx/color.hpp b/src/gfx/color.hpp
index aff7a1f7..dcbf1e24 100644
--- a/src/gfx/color.hpp
+++ b/src/gfx/color.hpp
@@ -32,6 +32,53 @@ class AColor : public Color {
inline bool operator != (const AColor& that) const { return ! (*this == that); }
};
+// -----------------------------------------------------------------------------
+// RGB Color, packed into 3 bytes
+// -----------------------------------------------------------------------------
+
+// stupid headers stealing useful names
+#undef RGB
+
+// it is important to pack this into 3 bytes, so we can directly convert from wxImage data
+#if defined(_MSC_VER)
+ #pragma pack(push, 1)
+ #define MAKE_PACKED
+#else
+ #define MAKE_PACKED __attribute__((__packed__))
+#endif
+
+/// An RGB triplet, packed into 3 bytes
+struct RGB {
+ Byte r,g,b;
+
+ RGB() {}
+ RGB(Byte x) : r(x), g(x), b(x) {}
+ RGB(Byte r, Byte g, Byte b) : r(r), g(g), b(b) {}
+ RGB(wxColour const& x) : r(x.Red()), g(x.Green()), b(x.Blue()) {}
+
+ inline int total() { return r+g+b; }
+
+ inline operator wxColour() const {
+ return wxColour(r,g,b);
+ }
+
+ inline bool operator == (RGB const& that) const {
+ return r == that.r && g == that.g && b == that.b;
+ }
+ inline bool operator < (RGB const& that) const {
+ if (r < that.r) return true;
+ if (r > that.r) return false;
+ if (g < that.g) return true;
+ if (g > that.g) return false;
+ return b < that.b;
+ }
+
+} MAKE_PACKED;
+
+#ifdef _MSC_VER
+ #pragma pack(pop)
+#endif
+
// ----------------------------------------------------------------------------- : Parsing
/// Parse a color
@@ -80,13 +127,13 @@ Color saturate(const Color& c, double amount);
* rgb(128,128,0) -> 0.5*cr + 0.5*cg
* rgb(128,0,0) -> 0.5*cr
*/
-Color recolor(Color const& c, Color const& cr, Color const& cg, Color const& cb, Color const& cw);
-Image recolor(Image const& im, Color const& cr, Color const& cg, Color const& cb, Color const& cw);
+RGB recolor(RGB x, RGB cr, RGB cg, RGB cb, RGB cw);
+void recolor(Image& img, RGB cr, RGB cg, RGB cb, RGB cw);
/// Like recolor: map green to similar black/white and blue to complementary white/black
-Image recolor(Image const& im, Color const& cr);
+void recolor(Image& img, RGB cr);
/// Fills an image with the specified color
-void fill_image(Image& image, const Color& color);
+void fill_image(Image& image, RGB color);
// ----------------------------------------------------------------------------- : EOF
#endif
diff --git a/src/gfx/generated_image.cpp b/src/gfx/generated_image.cpp
index 76de5902..697cd8e0 100644
--- a/src/gfx/generated_image.cpp
+++ b/src/gfx/generated_image.cpp
@@ -208,6 +208,19 @@ bool InvertImage::operator == (const GeneratedImage& that) const {
return that2 && *image == *that2->image;
}
+// ----------------------------------------------------------------------------- : RecolorImage
+
+Image RecolorImage::generate(const Options& opt) const {
+ Image img = image->generate(opt);
+ recolor(img, color);
+ return img;
+}
+bool RecolorImage::operator == (const GeneratedImage& that) const {
+ const RecolorImage* that2 = dynamic_cast(&that);
+ return that2 && *image == *that2->image
+ && color == that2->color;
+}
+
// ----------------------------------------------------------------------------- : FlipImage
Image FlipImageHorizontal::generate(const Options& opt) const {
diff --git a/src/gfx/generated_image.hpp b/src/gfx/generated_image.hpp
index a97645f0..d97a0343 100644
--- a/src/gfx/generated_image.hpp
+++ b/src/gfx/generated_image.hpp
@@ -214,6 +214,20 @@ class InvertImage : public SimpleFilterImage {
virtual bool operator == (const GeneratedImage& that) const;
};
+// ----------------------------------------------------------------------------- : RecolorImage
+
+/// Recolor an image
+class RecolorImage : public SimpleFilterImage {
+ public:
+ inline RecolorImage(const GeneratedImageP& image, Color color)
+ : SimpleFilterImage(image), color(color)
+ {}
+ virtual Image generate(const Options& opt) const;
+ virtual bool operator == (const GeneratedImage& that) const;
+ private:
+ Color color;
+};
+
// ----------------------------------------------------------------------------- : FlipImage
/// Flip an image horizontally
diff --git a/src/gfx/image_effects.cpp b/src/gfx/image_effects.cpp
index 0a6d4896..d907abb4 100644
--- a/src/gfx/image_effects.cpp
+++ b/src/gfx/image_effects.cpp
@@ -70,3 +70,43 @@ void invert(Image& img) {
data[i] = 255 - data[i];
}
}
+
+// ----------------------------------------------------------------------------- : Coloring symbol images
+
+RGB recolor(RGB x, RGB cr, RGB cg, RGB cb, RGB cw) {
+ int lo = min(x.r,min(x.g,x.b));
+ // amount of each
+ int nr = x.r - lo;
+ int ng = x.g - lo;
+ int nb = x.b - lo;
+ int nw = lo;
+ // We should have that nr+ng+bw+nw < 255,
+ // otherwise the input is not a mixture of red/green/blue/white.
+ // Just to be sure, divide by the sum instead of 255
+ int total = max(255, nr+ng+nb+nw);
+
+ return RGB(
+ static_cast( (nr * cr.r + ng * cg.r + nb * cb.r + nw * cw.r) / total ),
+ static_cast( (nr * cr.g + ng * cg.g + nb * cb.g + nw * cw.g) / total ),
+ static_cast( (nr * cr.b + ng * cg.b + nb * cb.b + nw * cw.b) / total )
+ );
+}
+
+void recolor(Image& img, RGB cr, RGB cg, RGB cb, RGB cw) {
+ RGB* data = (RGB*)img.GetData();
+ int n = img.GetWidth() * img.GetHeight();
+ for (int i = 0 ; i < n ; ++i) {
+ data[i] = recolor(data[i], cr, cg, cb, cw);
+ }
+}
+
+Byte to_grayscale(RGB x) {
+ return (Byte)((6969 * x.r + 23434 * x.g + 2365 * x.b) / 32768); // from libpng
+}
+
+void recolor(Image& img, RGB cr) {
+ RGB black(0,0,0), white(255,255,255);
+ bool dark = to_grayscale(cr) < 100;
+ recolor(img, cr, dark ? black : white, dark ? white : black, white);
+}
+
diff --git a/src/mse.vcproj b/src/mse.vcproj
index 7fa98e8d..235d9932 100644
--- a/src/mse.vcproj
+++ b/src/mse.vcproj
@@ -3631,6 +3631,9 @@
+
+
@@ -3658,6 +3661,9 @@
+
+
diff --git a/src/script/functions/image.cpp b/src/script/functions/image.cpp
index bd93c26f..d537fd84 100644
--- a/src/script/functions/image.cpp
+++ b/src/script/functions/image.cpp
@@ -91,6 +91,12 @@ SCRIPT_FUNCTION(invert_image) {
return intrusive(new InvertImage(input));
}
+SCRIPT_FUNCTION(recolor_image) {
+ SCRIPT_PARAM_C(GeneratedImageP, input);
+ SCRIPT_PARAM(Color, color);
+ return intrusive(new RecolorImage(input,color));
+}
+
SCRIPT_FUNCTION(enlarge) {
SCRIPT_PARAM_C(GeneratedImageP, input);
SCRIPT_PARAM_N(double, _("border size"), border_size);
@@ -212,6 +218,7 @@ void init_script_image_functions(Context& ctx) {
ctx.setVariable(_("set combine"), script_set_combine);
ctx.setVariable(_("saturate"), script_saturate);
ctx.setVariable(_("invert image"), script_invert_image);
+ ctx.setVariable(_("recolor image"), script_recolor_image);
ctx.setVariable(_("enlarge"), script_enlarge);
ctx.setVariable(_("crop"), script_crop);
ctx.setVariable(_("flip horizontal"), script_flip_horizontal);
diff --git a/src/util/prec.hpp b/src/util/prec.hpp
index 026f0936..f0ad2569 100644
--- a/src/util/prec.hpp
+++ b/src/util/prec.hpp
@@ -36,6 +36,8 @@
#include
using namespace std;
+#undef RGB
+
// ----------------------------------------------------------------------------- : Wx Aliasses
// Remove some of the wxUglyness