diff --git a/doc/function/flip_horizontal.txt b/doc/function/flip_horizontal.txt new file mode 100644 index 00000000..c3af2872 --- /dev/null +++ b/doc/function/flip_horizontal.txt @@ -0,0 +1,18 @@ +Function: flip_horizontal + +--Usage-- +> flip_horizontal(input: image) + +Flip an image horizontally. + +--Parameters-- +! Parameter Type Description +| @input@ [[type:image]] Image to flip. + +--Examples-- +> flip_horizontal("image_logo.png") == [[Image]] +>>> flip_horizontal("image_logo.png") == "image_logo_hflip.png" + +--See also-- +| [[fun:flip_vertical]] Flip an image vertically +| [[fun:rotate]] Rotate an image diff --git a/doc/function/flip_vertical.txt b/doc/function/flip_vertical.txt new file mode 100644 index 00000000..19c7b3d1 --- /dev/null +++ b/doc/function/flip_vertical.txt @@ -0,0 +1,18 @@ +Function: flip_vertical + +--Usage-- +> flip_vertical(input: image) + +Flip an image vertically. + +--Parameters-- +! Parameter Type Description +| @input@ [[type:image]] Image to flip. + +--Examples-- +> flip_vertical("image_logo.png") == [[Image]] +>>> flip_vertical("image_logo.png") == "image_logo_vflip.png" + +--See also-- +| [[fun:flip_horizontal]] Flip an image horizontally +| [[fun:rotate]] Rotate an image diff --git a/doc/function/image_logo.png b/doc/function/image_logo.png new file mode 100644 index 00000000..c64c192b Binary files /dev/null and b/doc/function/image_logo.png differ diff --git a/doc/function/image_logo_desaturate.png b/doc/function/image_logo_desaturate.png new file mode 100644 index 00000000..35c21f48 Binary files /dev/null and b/doc/function/image_logo_desaturate.png differ diff --git a/doc/function/image_logo_hflip.png b/doc/function/image_logo_hflip.png new file mode 100644 index 00000000..4e0ded3d Binary files /dev/null and b/doc/function/image_logo_hflip.png differ diff --git a/doc/function/image_logo_invert.png b/doc/function/image_logo_invert.png new file mode 100644 index 00000000..39e19ddd Binary files /dev/null and b/doc/function/image_logo_invert.png differ diff --git a/doc/function/image_logo_rotate30.png b/doc/function/image_logo_rotate30.png new file mode 100644 index 00000000..6799fbd5 Binary files /dev/null and b/doc/function/image_logo_rotate30.png differ diff --git a/doc/function/image_logo_vflip.png b/doc/function/image_logo_vflip.png new file mode 100644 index 00000000..6f459f64 Binary files /dev/null and b/doc/function/image_logo_vflip.png differ diff --git a/doc/function/index.txt b/doc/function/index.txt index 3a380b9e..7422aae4 100644 --- a/doc/function/index.txt +++ b/doc/function/index.txt @@ -83,8 +83,12 @@ 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: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. +| [[fun:flip_vertical]] Flip an image vertically. +| [[fun:rotate]] Rotate an image. | [[fun:drop_shadow]] Add a drop shadow to an image. | [[fun:symbol_variation]] Render a variation of a [[type:symbol]]. | [[fun:built_in_image]] Return an image built into the program. diff --git a/doc/function/invert.txt b/doc/function/invert.txt new file mode 100644 index 00000000..fdc0619f --- /dev/null +++ b/doc/function/invert.txt @@ -0,0 +1,14 @@ +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/rotate.txt b/doc/function/rotate.txt new file mode 100644 index 00000000..3519dce1 --- /dev/null +++ b/doc/function/rotate.txt @@ -0,0 +1,19 @@ +Function: flip_vertical + +--Usage-- +> rotate(input: image, angle: some_number) + +Rotate an image. The image can become larger to accomodate the rotated bounding box. + +--Parameters-- +! Parameter Type Description +| @input@ [[type:image]] Image to rotate. +| @angle@ [[type:double]] Angle to rotate by, in degrees counter clockwise. + +--Examples-- +> rotate("image_logo.png", angle:30) == [[Image]] +>>> rotate("image_logo.png", angle:30) == "image_logo_rotate30.png" + +--See also-- +| [[fun:flip_horizontal]] Flip an image horizontally +| [[fun:flip_vertical]] Flip an image vertically diff --git a/doc/function/saturate.txt b/doc/function/saturate.txt index f9f9f9cf..44b0d904 100644 --- a/doc/function/saturate.txt +++ b/doc/function/saturate.txt @@ -19,3 +19,5 @@ To desaturate use an amount between @0@ (no desaturation) and @-1@ (convert to g >>> saturate("image5.png", amount: 0.5) == "image_saturate1.png" > saturate("image5.png", amount: -0.5) == [[Image]] >>> saturate("image5.png", amount: -0.5) == "image_saturate2.png" +> saturate("image_logo.png", amount: -1) == [[Image]] +>>> saturate("image_logo.png", amount: -1) == "image_logo_desaturate.png" diff --git a/src/data/field/color.cpp b/src/data/field/color.cpp index 1c7d0708..82f33804 100644 --- a/src/data/field/color.cpp +++ b/src/data/field/color.cpp @@ -41,8 +41,13 @@ IMPLEMENT_REFLECTION(ColorField) { // ----------------------------------------------------------------------------- : ColorField::Choice IMPLEMENT_REFLECTION(ColorField::Choice) { - REFLECT(name); - REFLECT(color); + if (tag.reading() && !tag.isComplex()) { + REFLECT_NAMELESS(name); + color = parse_color(name); + } else { + REFLECT(name); + REFLECT(color); + } } // ----------------------------------------------------------------------------- : ColorStyle diff --git a/src/gfx/color.hpp b/src/gfx/color.hpp index 9c6a383f..aff7a1f7 100644 --- a/src/gfx/color.hpp +++ b/src/gfx/color.hpp @@ -60,9 +60,31 @@ Color hsl2rgb(double h, double s, double l); /// A darker version of a color Color darken(const Color& c); +/// A black or white color, that contrasts with c +Color contrasting_color(const Color& c); + /// A saturated version of a color Color saturate(const Color& c, double amount); +/// Recolor: +/** + * Maps + * black -> black + * red -> cr + * green -> cg + * blue -> cb + * white -> cw + * + * Allows for interpolation between the colors, so for example + * rgb(128,128,128) -> 0.5*cw + * 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); +/// Like recolor: map green to similar black/white and blue to complementary white/black +Image recolor(Image const& im, Color const& cr); + /// Fills an image with the specified color void fill_image(Image& image, const Color& color); diff --git a/src/gfx/generated_image.cpp b/src/gfx/generated_image.cpp index 1efbac25..76de5902 100644 --- a/src/gfx/generated_image.cpp +++ b/src/gfx/generated_image.cpp @@ -196,6 +196,48 @@ bool SaturateImage::operator == (const GeneratedImage& that) const { && amount == that2->amount; } +// ----------------------------------------------------------------------------- : InvertImage + +Image InvertImage::generate(const Options& opt) const { + Image img = image->generate(opt); + invert(img); + return img; +} +bool InvertImage::operator == (const GeneratedImage& that) const { + const InvertImage* that2 = dynamic_cast(&that); + return that2 && *image == *that2->image; +} + +// ----------------------------------------------------------------------------- : FlipImage + +Image FlipImageHorizontal::generate(const Options& opt) const { + Image img = image->generate(opt); + return flip_image_horizontal(img); +} +bool FlipImageHorizontal::operator == (const GeneratedImage& that) const { + const FlipImageHorizontal* that2 = dynamic_cast(&that); + return that2 && *image == *that2->image; +} + +Image FlipImageVertical::generate(const Options& opt) const { + Image img = image->generate(opt); + return flip_image_vertical(img); +} +bool FlipImageVertical::operator == (const GeneratedImage& that) const { + const FlipImageVertical* that2 = dynamic_cast(&that); + return that2 && *image == *that2->image; +} + +Image RotateImage::generate(const Options& opt) const { + Image img = image->generate(opt); + return rotate_image(img,angle); +} +bool RotateImage::operator == (const GeneratedImage& that) const { + const RotateImage* that2 = dynamic_cast(&that); + return that2 && *image == *that2->image + && angle == that2->angle; +} + // ----------------------------------------------------------------------------- : EnlargeImage Image EnlargeImage::generate(const Options& opt) const { diff --git a/src/gfx/generated_image.hpp b/src/gfx/generated_image.hpp index c2f9aa8b..a97645f0 100644 --- a/src/gfx/generated_image.hpp +++ b/src/gfx/generated_image.hpp @@ -202,6 +202,52 @@ class SaturateImage : public SimpleFilterImage { double amount; }; +// ----------------------------------------------------------------------------- : InvertImage + +/// Invert an image +class InvertImage : public SimpleFilterImage { + public: + inline InvertImage(const GeneratedImageP& image) + : SimpleFilterImage(image) + {} + virtual Image generate(const Options& opt) const; + virtual bool operator == (const GeneratedImage& that) const; +}; + +// ----------------------------------------------------------------------------- : FlipImage + +/// Flip an image horizontally +class FlipImageHorizontal : public SimpleFilterImage { + public: + inline FlipImageHorizontal(const GeneratedImageP& image) + : SimpleFilterImage(image) + {} + virtual Image generate(const Options& opt) const; + virtual bool operator == (const GeneratedImage& that) const; +}; + +/// Flip an image vertically +class FlipImageVertical : public SimpleFilterImage { + public: + inline FlipImageVertical(const GeneratedImageP& image) + : SimpleFilterImage(image) + {} + virtual Image generate(const Options& opt) const; + virtual bool operator == (const GeneratedImage& that) const; +}; + +/// Rotate an image +class RotateImage : public SimpleFilterImage { + public: + inline RotateImage(const GeneratedImageP& image, double angle) + : SimpleFilterImage(image), angle(angle) + {} + virtual Image generate(const Options& opt) const; + virtual bool operator == (const GeneratedImage& that) const; + private: + double angle; +}; + // ----------------------------------------------------------------------------- : EnlargeImage /// Enlarge an image by adding a border around it diff --git a/src/gfx/gfx.hpp b/src/gfx/gfx.hpp index 077f67e3..7ca61043 100644 --- a/src/gfx/gfx.hpp +++ b/src/gfx/gfx.hpp @@ -74,8 +74,12 @@ inline double rad_to_deg(double rad) { return rad * (180.0 / M_PI); } inline double deg_to_rad(double deg) { return deg * (M_PI / 180.0); } /// Rotates an image counter clockwise -/// angle must be a multiple of 90, i.e. {0,90,180,270} -Image rotate_image(const Image& image, int angle); +Image rotate_image(const Image& image, double angle); + +/// Flip an image horizontally +Image flip_image_horizontal(const Image& image); +/// Flip an image vertically +Image flip_image_vertical(const Image& image); // ----------------------------------------------------------------------------- : Blending @@ -98,6 +102,9 @@ void mask_blend(Image& img1, const Image& img2, const Image& mask); /// Saturate an image void saturate(Image& image, double amount); +/// Invert the colors in an image +void invert(Image& img); + // ----------------------------------------------------------------------------- : Combining /// Ways in which images can be combined, similair to what Photoshop supports diff --git a/src/gfx/image_effects.cpp b/src/gfx/image_effects.cpp index eebdf94d..0a6d4896 100644 --- a/src/gfx/image_effects.cpp +++ b/src/gfx/image_effects.cpp @@ -60,3 +60,13 @@ void saturate(Image& image, double amount) { } } } + +// ----------------------------------------------------------------------------- : Color inversion + +void invert(Image& img) { + Byte* data = img.GetData(); + int n = 3 * img.GetWidth() * img.GetHeight(); + for (int i = 0 ; i < n ; ++i) { + data[i] = 255 - data[i]; + } +} diff --git a/src/gfx/rotate_image.cpp b/src/gfx/rotate_image.cpp index e659bb37..76bfd590 100644 --- a/src/gfx/rotate_image.cpp +++ b/src/gfx/rotate_image.cpp @@ -83,24 +83,58 @@ struct Rotate270 { // ----------------------------------------------------------------------------- : Interface -Image rotate_image(const Image& image, int angle) { - switch (angle % 360) { - case 0: return image; - case 90: return rotate_image_impl (image); - case 180: return rotate_image_impl(image); - case 270: return rotate_image_impl(image); - default: - if (!image.HasAlpha()) const_cast(image).InitAlpha(); - return image.Rotate(angle * M_PI / 180, wxPoint(0,0)); +double almost_equal(double x, double y) { + return fabs(x-y) < 1e-6; +} + +Image rotate_image(const Image& image, double angle) { + double a = fmod(angle, 360); + if (almost_equal(a, 0)) return image; + if (almost_equal(a, 90)) return rotate_image_impl (image); + if (almost_equal(a,180)) return rotate_image_impl(image); + if (almost_equal(a,270)) return rotate_image_impl(image); + else { + if (!image.HasAlpha()) const_cast(image).InitAlpha(); + return image.Rotate(angle * M_PI / 180, wxPoint(0,0)); } } -/*Bitmap rotate_bitmap(const Bitmap& bitmap, int angle) { - switch (angle % 360) { - case 90: - case 180: - case 270: - default: return bitmap; + +// ----------------------------------------------------------------------------- : Flipping images + +// reverse a list of n chunks of size 'step' +void do_flip(Byte const* in, Byte* out, int step, int n) { + for (int i = 0, j = n-1 ; i < n ; ++i, --j) { + memcpy(&out[i*step], &in[j*step], step); } } -*/ +void do_flip(Byte const* in, Byte* out, int step1, int n1, int n2) { + int step2 = step1 * n1; + for (int i = 0 ; i < n2 ; ++i) { + do_flip(in,out,step1,n1); + in += step2; + out += step2; + } +} + +Image flip_image_horizontal(Image const& img) { + int w = img.GetWidth(), h= img.GetHeight(); + Image out(w,h,false); + do_flip(img.GetData(), out.GetData(), 3, w, h); + if (img.HasAlpha()) { + out.InitAlpha(); + do_flip(img.GetAlpha(), out.GetAlpha(), 1, w, h); + } + return out; +} + +Image flip_image_vertical(Image const& img) { + int w = img.GetWidth(), h= img.GetHeight(); + Image out(w,h,false); + do_flip(img.GetData(), out.GetData(), 3 * w, h); + if (img.HasAlpha()) { + out.InitAlpha(); + do_flip(img.GetAlpha(), out.GetAlpha(), 1 * w, h); + } + return out; +} diff --git a/src/script/functions/image.cpp b/src/script/functions/image.cpp index 09590ee0..bd93c26f 100644 --- a/src/script/functions/image.cpp +++ b/src/script/functions/image.cpp @@ -86,6 +86,11 @@ SCRIPT_FUNCTION(saturate) { return intrusive(new SaturateImage(input, amount)); } +SCRIPT_FUNCTION(invert_image) { + SCRIPT_PARAM_C(GeneratedImageP, input); + return intrusive(new InvertImage(input)); +} + SCRIPT_FUNCTION(enlarge) { SCRIPT_PARAM_C(GeneratedImageP, input); SCRIPT_PARAM_N(double, _("border size"), border_size); @@ -101,6 +106,22 @@ SCRIPT_FUNCTION(crop) { return intrusive(new CropImage(input, width, height, offset_x, offset_y)); } +SCRIPT_FUNCTION(flip_horizontal) { + SCRIPT_PARAM_C(GeneratedImageP, input); + return intrusive(new FlipImageHorizontal(input)); +} + +SCRIPT_FUNCTION(flip_vertical) { + SCRIPT_PARAM_C(GeneratedImageP, input); + return intrusive(new FlipImageVertical(input)); +} + +SCRIPT_FUNCTION(rotate) { + SCRIPT_PARAM_C(GeneratedImageP, input); + SCRIPT_PARAM_N(double, _("angle"), angle); + return intrusive(new RotateImage(input,angle)); +} + SCRIPT_FUNCTION(drop_shadow) { SCRIPT_PARAM_C(GeneratedImageP, input); SCRIPT_OPTIONAL_PARAM_N_(double, _("offset x"), offset_x); @@ -190,8 +211,12 @@ void init_script_image_functions(Context& ctx) { ctx.setVariable(_("set alpha"), script_set_alpha); ctx.setVariable(_("set combine"), script_set_combine); ctx.setVariable(_("saturate"), script_saturate); + ctx.setVariable(_("invert image"), script_invert_image); ctx.setVariable(_("enlarge"), script_enlarge); ctx.setVariable(_("crop"), script_crop); + ctx.setVariable(_("flip horizontal"), script_flip_horizontal); + ctx.setVariable(_("flip vertical"), script_flip_vertical); + ctx.setVariable(_("rotate"), script_rotate); ctx.setVariable(_("drop shadow"), script_drop_shadow); ctx.setVariable(_("symbol variation"), script_symbol_variation); ctx.setVariable(_("built in image"), script_built_in_image);