Commit ed333b53 authored by twanvl's avatar twanvl

Fix #10: Use of saturate function almost always causes a crash.

saturate: fix possible division by 0
SaturateImage: fix: had two 'image' members, one inherited, one nullptr, which caused a crash
parent c190eb8e
...@@ -228,9 +228,6 @@ Image EnlargeImage::generate(const Options& opt) const { ...@@ -228,9 +228,6 @@ Image EnlargeImage::generate(const Options& opt) const {
// done // done
return larger; return larger;
} }
ImageCombine EnlargeImage::combine() const {
return image->combine();
}
bool EnlargeImage::operator == (const GeneratedImage& that) const { bool EnlargeImage::operator == (const GeneratedImage& that) const {
const EnlargeImage* that2 = dynamic_cast<const EnlargeImage*>(&that); const EnlargeImage* that2 = dynamic_cast<const EnlargeImage*>(&that);
return that2 && *image == *that2->image return that2 && *image == *that2->image
...@@ -242,9 +239,6 @@ bool EnlargeImage::operator == (const GeneratedImage& that) const { ...@@ -242,9 +239,6 @@ bool EnlargeImage::operator == (const GeneratedImage& that) const {
Image CropImage::generate(const Options& opt) const { Image CropImage::generate(const Options& opt) const {
return image->generate(opt).Size(wxSize((int)width, (int)height), wxPoint(-(int)offset_x, -(int)offset_y)); return image->generate(opt).Size(wxSize((int)width, (int)height), wxPoint(-(int)offset_x, -(int)offset_y));
} }
ImageCombine CropImage::combine() const {
return image->combine();
}
bool CropImage::operator == (const GeneratedImage& that) const { bool CropImage::operator == (const GeneratedImage& that) const {
const CropImage* that2 = dynamic_cast<const CropImage*>(&that); const CropImage* that2 = dynamic_cast<const CropImage*>(&that);
return that2 && *image == *that2->image return that2 && *image == *that2->image
...@@ -340,9 +334,6 @@ Image DropShadowImage::generate(const Options& opt) const { ...@@ -340,9 +334,6 @@ Image DropShadowImage::generate(const Options& opt) const {
delete[] shadow; delete[] shadow;
return img; return img;
} }
ImageCombine DropShadowImage::combine() const {
return image->combine();
}
bool DropShadowImage::operator == (const GeneratedImage& that) const { bool DropShadowImage::operator == (const GeneratedImage& that) const {
const DropShadowImage* that2 = dynamic_cast<const DropShadowImage*>(&that); const DropShadowImage* that2 = dynamic_cast<const DropShadowImage*>(&that);
return that2 && *image == *that2->image return that2 && *image == *that2->image
......
...@@ -199,41 +199,34 @@ class SaturateImage : public SimpleFilterImage { ...@@ -199,41 +199,34 @@ class SaturateImage : public SimpleFilterImage {
virtual Image generate(const Options& opt) const; virtual Image generate(const Options& opt) const;
virtual bool operator == (const GeneratedImage& that) const; virtual bool operator == (const GeneratedImage& that) const;
private: private:
GeneratedImageP image;
double amount; double amount;
}; };
// ----------------------------------------------------------------------------- : EnlargeImage // ----------------------------------------------------------------------------- : EnlargeImage
/// Enlarge an image by adding a border around it /// Enlarge an image by adding a border around it
class EnlargeImage : public GeneratedImage { class EnlargeImage : public SimpleFilterImage {
public: public:
inline EnlargeImage(const GeneratedImageP& image, double border_size) inline EnlargeImage(const GeneratedImageP& image, double border_size)
: image(image), border_size(fabs(border_size)) : SimpleFilterImage(image), border_size(fabs(border_size))
{} {}
virtual Image generate(const Options& opt) const; virtual Image generate(const Options& opt) const;
virtual ImageCombine combine() const;
virtual bool operator == (const GeneratedImage& that) const; virtual bool operator == (const GeneratedImage& that) const;
virtual bool local() const { return image->local(); }
private: private:
GeneratedImageP image;
double border_size; double border_size;
}; };
// ----------------------------------------------------------------------------- : CropImage // ----------------------------------------------------------------------------- : CropImage
/// Crop an image at a certain point, to a certain size /// Crop an image at a certain point, to a certain size
class CropImage : public GeneratedImage { class CropImage : public SimpleFilterImage {
public: public:
inline CropImage(const GeneratedImageP& image, double width, double height, double offset_x, double offset_y) inline CropImage(const GeneratedImageP& image, double width, double height, double offset_x, double offset_y)
: image(image), width(width), height(height), offset_x(offset_x), offset_y(offset_y) : SimpleFilterImage(image), width(width), height(height), offset_x(offset_x), offset_y(offset_y)
{} {}
virtual Image generate(const Options& opt) const; virtual Image generate(const Options& opt) const;
virtual ImageCombine combine() const;
virtual bool operator == (const GeneratedImage& that) const; virtual bool operator == (const GeneratedImage& that) const;
virtual bool local() const { return image->local(); }
private: private:
GeneratedImageP image;
double width, height; double width, height;
double offset_x, offset_y; double offset_x, offset_y;
}; };
...@@ -241,18 +234,15 @@ class CropImage : public GeneratedImage { ...@@ -241,18 +234,15 @@ class CropImage : public GeneratedImage {
// ----------------------------------------------------------------------------- : DropShadowImage // ----------------------------------------------------------------------------- : DropShadowImage
/// Add a drop shadow to an image /// Add a drop shadow to an image
class DropShadowImage : public GeneratedImage { class DropShadowImage : public SimpleFilterImage {
public: public:
inline DropShadowImage(const GeneratedImageP& image, double offset_x, double offset_y, double shadow_alpha, double shadow_blur_radius, Color shadow_color) inline DropShadowImage(const GeneratedImageP& image, double offset_x, double offset_y, double shadow_alpha, double shadow_blur_radius, Color shadow_color)
: image(image), offset_x(offset_x), offset_y(offset_y) : SimpleFilterImage(image), offset_x(offset_x), offset_y(offset_y)
, shadow_alpha(shadow_alpha), shadow_blur_radius(shadow_blur_radius), shadow_color(shadow_color) , shadow_alpha(shadow_alpha), shadow_blur_radius(shadow_blur_radius), shadow_color(shadow_color)
{} {}
virtual Image generate(const Options& opt) const; virtual Image generate(const Options& opt) const;
virtual ImageCombine combine() const;
virtual bool operator == (const GeneratedImage& that) const; virtual bool operator == (const GeneratedImage& that) const;
virtual bool local() const { return image->local(); }
private: private:
GeneratedImageP image;
double offset_x, offset_y; double offset_x, offset_y;
double shadow_alpha; double shadow_alpha;
double shadow_blur_radius; double shadow_blur_radius;
......
...@@ -13,29 +13,42 @@ ...@@ -13,29 +13,42 @@
// ----------------------------------------------------------------------------- : Saturation // ----------------------------------------------------------------------------- : Saturation
void saturate(Image& image, double amount) { void saturate(Image& image, double amount) {
if (amount == 0) return; // nothing to do
Byte* pix = image.GetData(); Byte* pix = image.GetData();
Byte* end = pix + image.GetWidth() * image.GetHeight() * 3; Byte* end = pix + image.GetWidth() * image.GetHeight() * 3;
if (amount > 0) { // the formula for saturation is
amount = min(amount,0.99); // rgb' = (rgb - amount * avg) / (1 - amount)
int factor = int(256 * amount); // if amount >= 1 then this is some kind of inversion
int div = 768 - 3 * factor; // if amount > 0 then use formula directly
// if amount < 0 then de-saturate instead:
// rgb = rgb' + -amount*avg - -amount*rgb'
// = rgb' * (1 - -amount) + -amount*avg
// if amount < -1 then we are left with just the average
int factor = int(256 * amount);
if (factor == 0) {
return; // nothing to do
} else if (factor == 256) {
// super crazy saturation: division by zero
// if we take infty to be 255, then it is a >avg test
while (pix != end) { while (pix != end) {
int r = pix[0], g = pix[1], b = pix[2]; int r = pix[0], g = pix[1], b = pix[2];
int avg = factor*(r+g+b); pix[0] = r+r > g+b ? 255 : 0;
pix[0] = col((768*r - avg) / div); pix[1] = g+g > b+r ? 255 : 0;
pix[1] = col((768*g - avg) / div); pix[2] = b+b > r+g ? 255 : 0;
pix[2] = col((768*b - avg) / div);
pix += 3; pix += 3;
} }
} else if (amount < -0.99) { } else if (factor > 0) {
int div = 768 - 3 * factor;
assert(div > 0);
while (pix != end) { while (pix != end) {
int r = pix[0], g = pix[1], b = pix[2]; int r = pix[0], g = pix[1], b = pix[2];
pix[0] = pix[1] = pix[2] = (r+g+b)/3; 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; pix += 3;
} }
} else { } else {
int factor1 = int(256 * -amount); int factor1 = -factor;
int factor2 = 768 - 3*factor1; int factor2 = 768 - 3*factor1;
while (pix != end) { while (pix != end) {
int r = pix[0], g = pix[1], b = pix[2]; int r = pix[0], g = pix[1], b = pix[2];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment