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