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(
) ==
+
+--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(
) ==
+
+--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(
) ==
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(
, angle:30) ==
+
+--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(
, amount: 0.5) ==
> saturate("image5.png", amount: -0.5) == [[Image]]
>>> saturate(
, amount: -0.5) ==
+> saturate("image_logo.png", amount: -1) == [[Image]]
+>>> saturate(
, amount: -1) ==
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);