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("image_logo.png") == "image_logo_invert.png" 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("image_logo.png") == "image_logo_invert.png" 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("symbol1.png", color: rgb(180,0,0)) == "symbol1_red.png" +> recolor_image("symbol1.png", color: rgb(100,255,0)) == [[Image]] +>>> recolor_image("symbol1.png", color: rgb(100,255,0)) == "symbol1_green.png" 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