diff --git a/doc/function/image5.png b/doc/function/image5.png
new file mode 100644
index 00000000..437a8f81
Binary files /dev/null and b/doc/function/image5.png differ
diff --git a/doc/function/image_saturate1.png b/doc/function/image_saturate1.png
new file mode 100644
index 00000000..a0b761cc
Binary files /dev/null and b/doc/function/image_saturate1.png differ
diff --git a/doc/function/image_saturate2.png b/doc/function/image_saturate2.png
new file mode 100644
index 00000000..0940a8aa
Binary files /dev/null and b/doc/function/image_saturate2.png differ
diff --git a/doc/function/index.txt b/doc/function/index.txt
index 8608592e..fe34aebe 100644
--- a/doc/function/index.txt
+++ b/doc/function/index.txt
@@ -67,7 +67,8 @@ These functions are built into the program, other [[type:function]]s can be defi
| [[fun:combine_blend]] Blend two images together using a given [[type:combine|combining mode]].
| [[fun:set_mask]] Set the transparancy mask of an image.
| [[fun:set_alpha]] Change the transparency of an image.
-| [[fun:set_combine]] Chnage how the image should be combined with the background.
+| [[fun:set_combine]] Change how the image should be combined with the background.
+| [[fun:saturate]] Saturate/desaturate 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:drop_shadow]] Add a drop shadow to an image.
diff --git a/doc/function/saturate.txt b/doc/function/saturate.txt
new file mode 100644
index 00000000..f9f9f9cf
--- /dev/null
+++ b/doc/function/saturate.txt
@@ -0,0 +1,21 @@
+Function: saturate
+
+--Usage--
+> saturate(input: image, amount: saturation amount)
+
+Saturate or desaturate an image. Saturation makes the colors brighter, desaturation makes the image more grey.
+
+To saturate use an amount between @0@ (no saturation) and @1@ (super crazy, too much saturation).
+
+To desaturate use an amount between @0@ (no desaturation) and @-1@ (convert to greyscale).
+
+--Parameters--
+! Parameter Type Description
+| @input@ [[type:image]] Image to (de)saturate.
+| @alpha@ [[type:double]] Saturation factor.
+
+--Examples--
+> saturate("image5.png", amount: 0.5) == [[Image]]
+>>> saturate(
, amount: 0.5) ==
+> saturate("image5.png", amount: -0.5) == [[Image]]
+>>> saturate(
, amount: -0.5) ==
diff --git a/src/gfx/generated_image.cpp b/src/gfx/generated_image.cpp
index f8253798..4d5fbe03 100644
--- a/src/gfx/generated_image.cpp
+++ b/src/gfx/generated_image.cpp
@@ -74,7 +74,7 @@ Image conform_image(const Image& img, const GeneratedImage::Options& options) {
}
// saturate?
if (options.saturate) {
- saturate(image, 40);
+ saturate(image, .1);
}
options.width = image.GetWidth();
options.height = image.GetHeight();
@@ -159,9 +159,6 @@ Image SetMaskImage::generate(const Options& opt) const {
set_alpha(img, mask->generate(opt));
return img;
}
-ImageCombine SetMaskImage::combine() const {
- return image->combine();
-}
bool SetMaskImage::operator == (const GeneratedImage& that) const {
const SetMaskImage* that2 = dynamic_cast(&that);
return that2 && *image == *that2->image
@@ -173,9 +170,6 @@ Image SetAlphaImage::generate(const Options& opt) const {
set_alpha(img, alpha);
return img;
}
-ImageCombine SetAlphaImage::combine() const {
- return image->combine();
-}
bool SetAlphaImage::operator == (const GeneratedImage& that) const {
const SetAlphaImage* that2 = dynamic_cast(&that);
return that2 && *image == *that2->image
@@ -196,6 +190,19 @@ bool SetCombineImage::operator == (const GeneratedImage& that) const {
&& image_combine == that2->image_combine;
}
+// ----------------------------------------------------------------------------- : SaturateImage
+
+Image SaturateImage::generate(const Options& opt) const {
+ Image img = image->generate(opt);
+ saturate(img, amount);
+ return img;
+}
+bool SaturateImage::operator == (const GeneratedImage& that) const {
+ const SaturateImage* that2 = dynamic_cast(&that);
+ return that2 && *image == *that2->image
+ && amount == that2->amount;
+}
+
// ----------------------------------------------------------------------------- : EnlargeImage
Image EnlargeImage::generate(const Options& opt) const {
diff --git a/src/gfx/generated_image.hpp b/src/gfx/generated_image.hpp
index 3696d60d..8089a137 100644
--- a/src/gfx/generated_image.hpp
+++ b/src/gfx/generated_image.hpp
@@ -66,6 +66,20 @@ class GeneratedImage : public ScriptValue {
/// Resize an image to conform to the options
Image conform_image(const Image&, const GeneratedImage::Options&);
+// ----------------------------------------------------------------------------- : SimpleFilterImage
+
+/// Apply some filter to a single image
+class SimpleFilterImage : public GeneratedImage {
+ public:
+ inline SimpleFilterImage(const GeneratedImageP& image)
+ : image(image)
+ {}
+ virtual ImageCombine combine() const { return image->combine(); }
+ virtual bool local() const { return image->local(); }
+ protected:
+ GeneratedImageP image;
+};
+
// ----------------------------------------------------------------------------- : BlankImage
/// An image generator that returns a blank image
@@ -133,49 +147,57 @@ class CombineBlendImage : public GeneratedImage {
// ----------------------------------------------------------------------------- : SetMaskImage
/// Change the alpha channel of an image
-class SetMaskImage : public GeneratedImage {
+class SetMaskImage : public SimpleFilterImage {
public:
inline SetMaskImage(const GeneratedImageP& image, const GeneratedImageP& mask)
- : image(image), mask(mask)
+ : SimpleFilterImage(image), mask(mask)
{}
virtual Image generate(const Options& opt) const;
- virtual ImageCombine combine() const;
virtual bool operator == (const GeneratedImage& that) const;
- virtual bool local() const { return image->local() && mask->local(); }
private:
- GeneratedImageP image, mask;
+ GeneratedImageP mask;
};
/// Change the alpha channel of an image
-class SetAlphaImage : public GeneratedImage {
+class SetAlphaImage : public SimpleFilterImage {
public:
inline SetAlphaImage(const GeneratedImageP& image, double alpha)
- : image(image), alpha(alpha)
+ : SimpleFilterImage(image), alpha(alpha)
{}
virtual Image generate(const Options& opt) const;
- virtual ImageCombine combine() const;
virtual bool operator == (const GeneratedImage& that) const;
- virtual bool local() const { return image->local(); }
private:
- GeneratedImageP image;
double alpha;
};
// ----------------------------------------------------------------------------- : SetCombineImage
/// Change the combine mode
-class SetCombineImage : public GeneratedImage {
+class SetCombineImage : public SimpleFilterImage {
public:
inline SetCombineImage(const GeneratedImageP& image, ImageCombine image_combine)
- : image(image), image_combine(image_combine)
+ : SimpleFilterImage(image), image_combine(image_combine)
{}
virtual Image generate(const Options& opt) const;
virtual ImageCombine combine() const;
virtual bool operator == (const GeneratedImage& that) const;
- virtual bool local() const { return image->local(); }
+ private:
+ ImageCombine image_combine;
+};
+
+// ----------------------------------------------------------------------------- : SaturateImage
+
+/// Saturate/desaturate an image
+class SaturateImage : public SimpleFilterImage {
+ public:
+ inline SaturateImage(const GeneratedImageP& image, double alpha)
+ : SimpleFilterImage(image), amount(amount)
+ {}
+ virtual Image generate(const Options& opt) const;
+ virtual bool operator == (const GeneratedImage& that) const;
private:
GeneratedImageP image;
- ImageCombine image_combine;
+ double amount;
};
// ----------------------------------------------------------------------------- : EnlargeImage
diff --git a/src/gfx/gfx.hpp b/src/gfx/gfx.hpp
index d74a4d6d..86693859 100644
--- a/src/gfx/gfx.hpp
+++ b/src/gfx/gfx.hpp
@@ -92,11 +92,8 @@ void mask_blend(Image& img1, const Image& img2, const Image& mask);
// ----------------------------------------------------------------------------- : Effects
-/// Saturate an image, amount should be in range [0...100]
-void saturate(Image& image, int amount);
-
-/// Desaturate an image
-void desaturate(Image& image);
+/// Saturate an image
+void saturate(Image& image, double amount);
// ----------------------------------------------------------------------------- : Combining
diff --git a/src/gfx/image_effects.cpp b/src/gfx/image_effects.cpp
index 1e3190ab..be7a2255 100644
--- a/src/gfx/image_effects.cpp
+++ b/src/gfx/image_effects.cpp
@@ -12,33 +12,38 @@
// ----------------------------------------------------------------------------- : Saturation
-void saturate(Image& image, int amount) {
+void saturate(Image& image, double amount) {
if (amount == 0) return; // nothing to do
- int factor = 300 / amount;
- int div = factor - 2;
- // for each pixel...
Byte* pix = image.GetData();
Byte* end = pix + image.GetWidth() * image.GetHeight() * 3;
- while (pix != end) {
- int r = pix[0], g = pix[1], b = pix[2];
- int r2 = (factor * r - g - b) / div;
- int g2 = (factor * g - r - b) / div;
- int b2 = (factor * b - r - g) / div;
- pix[0] = col(r2);
- pix[1] = col(g2);
- pix[2] = col(b2);
- pix += 3;
- }
-}
-
-void desaturate(Image& image/*, int amount*/) {
- Byte* pix = image.GetData();
- Byte* end = pix + image.GetWidth() * image.GetHeight() * 3;
- while (pix != end) {
- int r = pix[0], g = pix[1], b = pix[2];
- pix[0] = (r+r+g+b) / 4;
- pix[1] = (g+r+g+b) / 4;
- pix[2] = (b+r+g+b) / 4;
- pix += 3;
+ if (amount > 0) {
+ amount = min(amount,0.99);
+ int factor = 256 * amount;
+ int div = 768 - 3 * factor;
+ while (pix != end) {
+ int r = pix[0], g = pix[1], b = pix[2];
+ int avg = factor*(r+g+b);
+ pix[0] = col((768*r - avg) / div);
+ pix[1] = col((768*g - avg) / div);
+ pix[2] = col((768*b - avg) / div);
+ pix += 3;
+ }
+ } else if (amount < -0.99) {
+ while (pix != end) {
+ int r = pix[0], g = pix[1], b = pix[2];
+ pix[0] = pix[1] = pix[2] = (r+g+b)/3;
+ pix += 3;
+ }
+ } else {
+ int factor1 = 256 * -amount;
+ int factor2 = 768 - 3*factor1;
+ while (pix != end) {
+ int r = pix[0], g = pix[1], b = pix[2];
+ int avg = factor1*(r+g+b);
+ pix[0] = (factor2*r + avg) / 768;
+ pix[1] = (factor2*g + avg) / 768;
+ pix[2] = (factor2*b + avg) / 768;
+ pix += 3;
+ }
}
}
diff --git a/src/gui/packages_window.cpp b/src/gui/packages_window.cpp
index 68a943c9..2e9d5275 100644
--- a/src/gui/packages_window.cpp
+++ b/src/gui/packages_window.cpp
@@ -277,7 +277,7 @@ class PackageIconRequest : public ThumbnailRequest {
Image resampled(16,16,false);
resample_preserve_aspect(image,resampled);
ti->icon = Bitmap(resampled);
- desaturate(resampled);
+ saturate(resampled, -.75);
set_alpha(resampled,0.5);
ti->icon_grey = Bitmap(resampled);
list->Refresh(false);
@@ -318,7 +318,7 @@ void PackageUpdateList::initItems() {
image = load_resource_image(_("installer_group"));
}
ti.icon = Bitmap(image);
- desaturate(image);
+ saturate(image, -.75);
set_alpha(image, 0.5);
ti.icon_grey = Bitmap(image);
if (p && !p->description->icon.Ok() && !p->description->icon_url.empty()) {
diff --git a/src/script/functions/image.cpp b/src/script/functions/image.cpp
index f29bf60d..161b5029 100644
--- a/src/script/functions/image.cpp
+++ b/src/script/functions/image.cpp
@@ -79,6 +79,12 @@ SCRIPT_FUNCTION(set_combine) {
return new_intrusive2(input, image_combine);
}
+SCRIPT_FUNCTION(saturate) {
+ SCRIPT_PARAM_C(GeneratedImageP, input);
+ SCRIPT_PARAM(double, amount);
+ return new_intrusive2(input, amount);
+}
+
SCRIPT_FUNCTION(enlarge) {
SCRIPT_PARAM_C(GeneratedImageP, input);
SCRIPT_PARAM_N(double, _("border size"), border_size);
@@ -170,6 +176,7 @@ void init_script_image_functions(Context& ctx) {
ctx.setVariable(_("set mask"), script_set_mask);
ctx.setVariable(_("set alpha"), script_set_alpha);
ctx.setVariable(_("set combine"), script_set_combine);
+ ctx.setVariable(_("saturate"), script_saturate);
ctx.setVariable(_("enlarge"), script_enlarge);
ctx.setVariable(_("crop"), script_crop);
ctx.setVariable(_("drop shadow"), script_drop_shadow);