//---------------------------------------------------------------------------- // Anti-Grain Geometry - Version 2.4 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) // // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied // warranty, and with no claim as to its suitability for any purpose. // //---------------------------------------------------------------------------- // // Adaptation for high precision colors has been sponsored by // Liberty Technology Systems, Inc., visit http://lib-sys.com // // Liberty Technology Systems, Inc. is the provider of // PostScript and PDF technology for software developers. // //---------------------------------------------------------------------------- // Contact: mcseem@antigrain.com // mcseemagg@yahoo.com // http://www.antigrain.com //---------------------------------------------------------------------------- #ifndef AGG_COLOR_RGBA_INCLUDED #define AGG_COLOR_RGBA_INCLUDED #include #include "agg_basics.h" #include "agg_gamma_lut.h" namespace agg { // Supported component orders for RGB and RGBA pixel formats //======================================================================= struct order_rgb { enum rgb_e { R=0, G=1, B=2, N=3 }; }; struct order_bgr { enum bgr_e { B=0, G=1, R=2, N=3 }; }; struct order_rgba { enum rgba_e { R=0, G=1, B=2, A=3, N=4 }; }; struct order_argb { enum argb_e { A=0, R=1, G=2, B=3, N=4 }; }; struct order_abgr { enum abgr_e { A=0, B=1, G=2, R=3, N=4 }; }; struct order_bgra { enum bgra_e { B=0, G=1, R=2, A=3, N=4 }; }; // Colorspace tag types. struct linear {}; struct sRGB {}; //====================================================================rgba struct rgba { typedef double value_type; double r; double g; double b; double a; //-------------------------------------------------------------------- rgba() {} //-------------------------------------------------------------------- rgba(double r_, double g_, double b_, double a_=1.0) : r(r_), g(g_), b(b_), a(a_) {} //-------------------------------------------------------------------- rgba(const rgba& c, double a_) : r(c.r), g(c.g), b(c.b), a(a_) {} //-------------------------------------------------------------------- rgba& clear() { r = g = b = a = 0; return *this; } //-------------------------------------------------------------------- rgba& transparent() { a = 0; return *this; } //-------------------------------------------------------------------- rgba& opacity(double a_) { if (a_ < 0) a = 0; else if (a_ > 1) a = 1; else a = a_; return *this; } //-------------------------------------------------------------------- double opacity() const { return a; } //-------------------------------------------------------------------- rgba& premultiply() { r *= a; g *= a; b *= a; return *this; } //-------------------------------------------------------------------- rgba& premultiply(double a_) { if (a <= 0 || a_ <= 0) { r = g = b = a = 0; } else { a_ /= a; r *= a_; g *= a_; b *= a_; a = a_; } return *this; } //-------------------------------------------------------------------- rgba& demultiply() { if (a == 0) { r = g = b = 0; } else { double a_ = 1.0 / a; r *= a_; g *= a_; b *= a_; } return *this; } //-------------------------------------------------------------------- rgba gradient(rgba c, double k) const { rgba ret; ret.r = r + (c.r - r) * k; ret.g = g + (c.g - g) * k; ret.b = b + (c.b - b) * k; ret.a = a + (c.a - a) * k; return ret; } rgba& operator+=(const rgba& c) { r += c.r; g += c.g; b += c.b; a += c.a; return *this; } rgba& operator*=(double k) { r *= k; g *= k; b *= k; a *= k; return *this; } //-------------------------------------------------------------------- static rgba no_color() { return rgba(0,0,0,0); } //-------------------------------------------------------------------- static rgba from_wavelength(double wl, double gamma = 1.0); //-------------------------------------------------------------------- explicit rgba(double wavelen, double gamma=1.0) { *this = from_wavelength(wavelen, gamma); } }; inline rgba operator+(const rgba& a, const rgba& b) { return rgba(a) += b; } inline rgba operator*(const rgba& a, double b) { return rgba(a) *= b; } //------------------------------------------------------------------------ inline rgba rgba::from_wavelength(double wl, double gamma) { rgba t(0.0, 0.0, 0.0); if (wl >= 380.0 && wl <= 440.0) { t.r = -1.0 * (wl - 440.0) / (440.0 - 380.0); t.b = 1.0; } else if (wl >= 440.0 && wl <= 490.0) { t.g = (wl - 440.0) / (490.0 - 440.0); t.b = 1.0; } else if (wl >= 490.0 && wl <= 510.0) { t.g = 1.0; t.b = -1.0 * (wl - 510.0) / (510.0 - 490.0); } else if (wl >= 510.0 && wl <= 580.0) { t.r = (wl - 510.0) / (580.0 - 510.0); t.g = 1.0; } else if (wl >= 580.0 && wl <= 645.0) { t.r = 1.0; t.g = -1.0 * (wl - 645.0) / (645.0 - 580.0); } else if (wl >= 645.0 && wl <= 780.0) { t.r = 1.0; } double s = 1.0; if (wl > 700.0) s = 0.3 + 0.7 * (780.0 - wl) / (780.0 - 700.0); else if (wl < 420.0) s = 0.3 + 0.7 * (wl - 380.0) / (420.0 - 380.0); t.r = std::pow(t.r * s, gamma); t.g = std::pow(t.g * s, gamma); t.b = std::pow(t.b * s, gamma); return t; } inline rgba rgba_pre(double r, double g, double b, double a) { return rgba(r, g, b, a).premultiply(); } //===================================================================rgba8 template struct rgba8T { typedef int8u value_type; typedef int32u calc_type; typedef int32 long_type; enum base_scale_e { base_shift = 8, base_scale = 1 << base_shift, base_mask = base_scale - 1, base_MSB = 1 << (base_shift - 1) }; typedef rgba8T self_type; value_type r; value_type g; value_type b; value_type a; static void convert(rgba8T& dst, const rgba8T& src) { dst.r = sRGB_conv::rgb_from_sRGB(src.r); dst.g = sRGB_conv::rgb_from_sRGB(src.g); dst.b = sRGB_conv::rgb_from_sRGB(src.b); dst.a = src.a; } static void convert(rgba8T& dst, const rgba8T& src) { dst.r = sRGB_conv::rgb_to_sRGB(src.r); dst.g = sRGB_conv::rgb_to_sRGB(src.g); dst.b = sRGB_conv::rgb_to_sRGB(src.b); dst.a = src.a; } static void convert(rgba8T& dst, const rgba& src) { dst.r = value_type(uround(src.r * base_mask)); dst.g = value_type(uround(src.g * base_mask)); dst.b = value_type(uround(src.b * base_mask)); dst.a = value_type(uround(src.a * base_mask)); } static void convert(rgba8T& dst, const rgba& src) { // Use the "float" table. dst.r = sRGB_conv::rgb_to_sRGB(float(src.r)); dst.g = sRGB_conv::rgb_to_sRGB(float(src.g)); dst.b = sRGB_conv::rgb_to_sRGB(float(src.b)); dst.a = sRGB_conv::alpha_to_sRGB(float(src.a)); } static void convert(rgba& dst, const rgba8T& src) { dst.r = src.r / 255.0; dst.g = src.g / 255.0; dst.b = src.b / 255.0; dst.a = src.a / 255.0; } static void convert(rgba& dst, const rgba8T& src) { // Use the "float" table. dst.r = sRGB_conv::rgb_from_sRGB(src.r); dst.g = sRGB_conv::rgb_from_sRGB(src.g); dst.b = sRGB_conv::rgb_from_sRGB(src.b); dst.a = sRGB_conv::alpha_from_sRGB(src.a); } //-------------------------------------------------------------------- rgba8T() {} //-------------------------------------------------------------------- rgba8T(unsigned r_, unsigned g_, unsigned b_, unsigned a_ = base_mask) : r(value_type(r_)), g(value_type(g_)), b(value_type(b_)), a(value_type(a_)) {} //-------------------------------------------------------------------- rgba8T(const rgba& c) { convert(*this, c); } //-------------------------------------------------------------------- rgba8T(const self_type& c, unsigned a_) : r(c.r), g(c.g), b(c.b), a(value_type(a_)) {} //-------------------------------------------------------------------- template rgba8T(const rgba8T& c) { convert(*this, c); } //-------------------------------------------------------------------- operator rgba() const { rgba c; convert(c, *this); return c; } //-------------------------------------------------------------------- static AGG_INLINE double to_double(value_type a) { return double(a) / base_mask; } //-------------------------------------------------------------------- static AGG_INLINE value_type from_double(double a) { return value_type(uround(a * base_mask)); } //-------------------------------------------------------------------- static AGG_INLINE value_type empty_value() { return 0; } //-------------------------------------------------------------------- static AGG_INLINE value_type full_value() { return base_mask; } //-------------------------------------------------------------------- AGG_INLINE bool is_transparent() const { return a == 0; } //-------------------------------------------------------------------- AGG_INLINE bool is_opaque() const { return a == base_mask; } //-------------------------------------------------------------------- static AGG_INLINE value_type invert(value_type x) { return base_mask - x; } //-------------------------------------------------------------------- // Fixed-point multiply, exact over int8u. static AGG_INLINE value_type multiply(value_type a, value_type b) { calc_type t = a * b + base_MSB; return value_type(((t >> base_shift) + t) >> base_shift); } //-------------------------------------------------------------------- static AGG_INLINE value_type demultiply(value_type a, value_type b) { if (a * b == 0) { return 0; } else if (a >= b) { return base_mask; } else return value_type((a * base_mask + (b >> 1)) / b); } //-------------------------------------------------------------------- template static AGG_INLINE T downscale(T a) { return a >> base_shift; } //-------------------------------------------------------------------- template static AGG_INLINE T downshift(T a, unsigned n) { return a >> n; } //-------------------------------------------------------------------- // Fixed-point multiply, exact over int8u. // Specifically for multiplying a color component by a cover. static AGG_INLINE value_type mult_cover(value_type a, cover_type b) { return multiply(a, b); } //-------------------------------------------------------------------- static AGG_INLINE cover_type scale_cover(cover_type a, value_type b) { return multiply(b, a); } //-------------------------------------------------------------------- // Interpolate p to q by a, assuming q is premultiplied by a. static AGG_INLINE value_type prelerp(value_type p, value_type q, value_type a) { return p + q - multiply(p, a); } //-------------------------------------------------------------------- // Interpolate p to q by a. static AGG_INLINE value_type lerp(value_type p, value_type q, value_type a) { int t = (q - p) * a + base_MSB - (p > q); return value_type(p + (((t >> base_shift) + t) >> base_shift)); } //-------------------------------------------------------------------- self_type& clear() { r = g = b = a = 0; return *this; } //-------------------------------------------------------------------- self_type& transparent() { a = 0; return *this; } //-------------------------------------------------------------------- self_type& opacity(double a_) { if (a_ < 0) a = 0; else if (a_ > 1) a = 1; else a = (value_type)uround(a_ * double(base_mask)); return *this; } //-------------------------------------------------------------------- double opacity() const { return double(a) / double(base_mask); } //-------------------------------------------------------------------- AGG_INLINE self_type& premultiply() { if (a != base_mask) { if (a == 0) { r = g = b = 0; } else { r = multiply(r, a); g = multiply(g, a); b = multiply(b, a); } } return *this; } //-------------------------------------------------------------------- AGG_INLINE self_type& premultiply(unsigned a_) { if (a != base_mask || a_ < base_mask) { if (a == 0 || a_ == 0) { r = g = b = a = 0; } else { calc_type r_ = (calc_type(r) * a_) / a; calc_type g_ = (calc_type(g) * a_) / a; calc_type b_ = (calc_type(b) * a_) / a; r = value_type((r_ > a_) ? a_ : r_); g = value_type((g_ > a_) ? a_ : g_); b = value_type((b_ > a_) ? a_ : b_); a = value_type(a_); } } return *this; } //-------------------------------------------------------------------- AGG_INLINE self_type& demultiply() { if (a < base_mask) { if (a == 0) { r = g = b = 0; } else { calc_type r_ = (calc_type(r) * base_mask) / a; calc_type g_ = (calc_type(g) * base_mask) / a; calc_type b_ = (calc_type(b) * base_mask) / a; r = value_type((r_ > calc_type(base_mask)) ? calc_type(base_mask) : r_); g = value_type((g_ > calc_type(base_mask)) ? calc_type(base_mask) : g_); b = value_type((b_ > calc_type(base_mask)) ? calc_type(base_mask) : b_); } } return *this; } //-------------------------------------------------------------------- AGG_INLINE self_type gradient(const self_type& c, double k) const { self_type ret; calc_type ik = uround(k * base_mask); ret.r = lerp(r, c.r, ik); ret.g = lerp(g, c.g, ik); ret.b = lerp(b, c.b, ik); ret.a = lerp(a, c.a, ik); return ret; } //-------------------------------------------------------------------- AGG_INLINE void add(const self_type& c, unsigned cover) { calc_type cr, cg, cb, ca; if (cover == cover_mask) { if (c.a == base_mask) { *this = c; return; } else { cr = r + c.r; cg = g + c.g; cb = b + c.b; ca = a + c.a; } } else { cr = r + mult_cover(c.r, cover); cg = g + mult_cover(c.g, cover); cb = b + mult_cover(c.b, cover); ca = a + mult_cover(c.a, cover); } r = (value_type)((cr > calc_type(base_mask)) ? calc_type(base_mask) : cr); g = (value_type)((cg > calc_type(base_mask)) ? calc_type(base_mask) : cg); b = (value_type)((cb > calc_type(base_mask)) ? calc_type(base_mask) : cb); a = (value_type)((ca > calc_type(base_mask)) ? calc_type(base_mask) : ca); } //-------------------------------------------------------------------- template AGG_INLINE void apply_gamma_dir(const GammaLUT& gamma) { r = gamma.dir(r); g = gamma.dir(g); b = gamma.dir(b); } //-------------------------------------------------------------------- template AGG_INLINE void apply_gamma_inv(const GammaLUT& gamma) { r = gamma.inv(r); g = gamma.inv(g); b = gamma.inv(b); } //-------------------------------------------------------------------- static self_type no_color() { return self_type(0,0,0,0); } //-------------------------------------------------------------------- static self_type from_wavelength(double wl, double gamma = 1.0) { return self_type(rgba::from_wavelength(wl, gamma)); } }; typedef rgba8T rgba8; typedef rgba8T srgba8; //-------------------------------------------------------------rgb8_packed inline rgba8 rgb8_packed(unsigned v) { return rgba8((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); } //-------------------------------------------------------------bgr8_packed inline rgba8 bgr8_packed(unsigned v) { return rgba8(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF); } //------------------------------------------------------------argb8_packed inline rgba8 argb8_packed(unsigned v) { return rgba8((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF, v >> 24); } //---------------------------------------------------------rgba8_gamma_dir template rgba8 rgba8_gamma_dir(rgba8 c, const GammaLUT& gamma) { return rgba8(gamma.dir(c.r), gamma.dir(c.g), gamma.dir(c.b), c.a); } //---------------------------------------------------------rgba8_gamma_inv template rgba8 rgba8_gamma_inv(rgba8 c, const GammaLUT& gamma) { return rgba8(gamma.inv(c.r), gamma.inv(c.g), gamma.inv(c.b), c.a); } //==================================================================rgba16 struct rgba16 { typedef int16u value_type; typedef int32u calc_type; typedef int64 long_type; enum base_scale_e { base_shift = 16, base_scale = 1 << base_shift, base_mask = base_scale - 1, base_MSB = 1 << (base_shift - 1) }; typedef rgba16 self_type; value_type r; value_type g; value_type b; value_type a; //-------------------------------------------------------------------- rgba16() {} //-------------------------------------------------------------------- rgba16(unsigned r_, unsigned g_, unsigned b_, unsigned a_=base_mask) : r(value_type(r_)), g(value_type(g_)), b(value_type(b_)), a(value_type(a_)) {} //-------------------------------------------------------------------- rgba16(const self_type& c, unsigned a_) : r(c.r), g(c.g), b(c.b), a(value_type(a_)) {} //-------------------------------------------------------------------- rgba16(const rgba& c) : r((value_type)uround(c.r * double(base_mask))), g((value_type)uround(c.g * double(base_mask))), b((value_type)uround(c.b * double(base_mask))), a((value_type)uround(c.a * double(base_mask))) {} //-------------------------------------------------------------------- rgba16(const rgba8& c) : r(value_type((value_type(c.r) << 8) | c.r)), g(value_type((value_type(c.g) << 8) | c.g)), b(value_type((value_type(c.b) << 8) | c.b)), a(value_type((value_type(c.a) << 8) | c.a)) {} //-------------------------------------------------------------------- rgba16(const srgba8& c) : r(sRGB_conv::rgb_from_sRGB(c.r)), g(sRGB_conv::rgb_from_sRGB(c.g)), b(sRGB_conv::rgb_from_sRGB(c.b)), a(sRGB_conv::alpha_from_sRGB(c.a)) {} //-------------------------------------------------------------------- operator rgba() const { return rgba( r / 65535.0, g / 65535.0, b / 65535.0, a / 65535.0); } //-------------------------------------------------------------------- operator rgba8() const { return rgba8(r >> 8, g >> 8, b >> 8, a >> 8); } //-------------------------------------------------------------------- operator srgba8() const { // Return (non-premultiplied) sRGB values. return srgba8( sRGB_conv::rgb_to_sRGB(r), sRGB_conv::rgb_to_sRGB(g), sRGB_conv::rgb_to_sRGB(b), sRGB_conv::alpha_to_sRGB(a)); } //-------------------------------------------------------------------- static AGG_INLINE double to_double(value_type a) { return double(a) / base_mask; } //-------------------------------------------------------------------- static AGG_INLINE value_type from_double(double a) { return value_type(uround(a * base_mask)); } //-------------------------------------------------------------------- static AGG_INLINE value_type empty_value() { return 0; } //-------------------------------------------------------------------- static AGG_INLINE value_type full_value() { return base_mask; } //-------------------------------------------------------------------- AGG_INLINE bool is_transparent() const { return a == 0; } //-------------------------------------------------------------------- AGG_INLINE bool is_opaque() const { return a == base_mask; } //-------------------------------------------------------------------- static AGG_INLINE value_type invert(value_type x) { return base_mask - x; } //-------------------------------------------------------------------- // Fixed-point multiply, exact over int16u. static AGG_INLINE value_type multiply(value_type a, value_type b) { calc_type t = a * b + base_MSB; return value_type(((t >> base_shift) + t) >> base_shift); } //-------------------------------------------------------------------- static AGG_INLINE value_type demultiply(value_type a, value_type b) { if (a * b == 0) { return 0; } else if (a >= b) { return base_mask; } else return value_type((a * base_mask + (b >> 1)) / b); } //-------------------------------------------------------------------- template static AGG_INLINE T downscale(T a) { return a >> base_shift; } //-------------------------------------------------------------------- template static AGG_INLINE T downshift(T a, unsigned n) { return a >> n; } //-------------------------------------------------------------------- // Fixed-point multiply, almost exact over int16u. // Specifically for multiplying a color component by a cover. static AGG_INLINE value_type mult_cover(value_type a, cover_type b) { return multiply(a, (b << 8) | b); } //-------------------------------------------------------------------- static AGG_INLINE cover_type scale_cover(cover_type a, value_type b) { return multiply((a << 8) | a, b) >> 8; } //-------------------------------------------------------------------- // Interpolate p to q by a, assuming q is premultiplied by a. static AGG_INLINE value_type prelerp(value_type p, value_type q, value_type a) { return p + q - multiply(p, a); } //-------------------------------------------------------------------- // Interpolate p to q by a. static AGG_INLINE value_type lerp(value_type p, value_type q, value_type a) { int t = (q - p) * a + base_MSB - (p > q); return value_type(p + (((t >> base_shift) + t) >> base_shift)); } //-------------------------------------------------------------------- self_type& clear() { r = g = b = a = 0; return *this; } //-------------------------------------------------------------------- self_type& transparent() { a = 0; return *this; } //-------------------------------------------------------------------- AGG_INLINE self_type& opacity(double a_) { if (a_ < 0) a = 0; if (a_ > 1) a = 1; a = value_type(uround(a_ * double(base_mask))); return *this; } //-------------------------------------------------------------------- double opacity() const { return double(a) / double(base_mask); } //-------------------------------------------------------------------- AGG_INLINE self_type& premultiply() { if (a != base_mask) { if (a == 0) { r = g = b = 0; } else { r = multiply(r, a); g = multiply(g, a); b = multiply(b, a); } } return *this; } //-------------------------------------------------------------------- AGG_INLINE self_type& premultiply(unsigned a_) { if (a < base_mask || a_ < base_mask) { if (a == 0 || a_ == 0) { r = g = b = a = 0; } else { calc_type r_ = (calc_type(r) * a_) / a; calc_type g_ = (calc_type(g) * a_) / a; calc_type b_ = (calc_type(b) * a_) / a; r = value_type((r_ > a_) ? a_ : r_); g = value_type((g_ > a_) ? a_ : g_); b = value_type((b_ > a_) ? a_ : b_); a = value_type(a_); } } return *this; } //-------------------------------------------------------------------- AGG_INLINE self_type& demultiply() { if (a < base_mask) { if (a == 0) { r = g = b = 0; } else { calc_type r_ = (calc_type(r) * base_mask) / a; calc_type g_ = (calc_type(g) * base_mask) / a; calc_type b_ = (calc_type(b) * base_mask) / a; r = value_type((r_ > calc_type(base_mask)) ? calc_type(base_mask) : r_); g = value_type((g_ > calc_type(base_mask)) ? calc_type(base_mask) : g_); b = value_type((b_ > calc_type(base_mask)) ? calc_type(base_mask) : b_); } } return *this; } //-------------------------------------------------------------------- AGG_INLINE self_type gradient(const self_type& c, double k) const { self_type ret; calc_type ik = uround(k * base_mask); ret.r = lerp(r, c.r, ik); ret.g = lerp(g, c.g, ik); ret.b = lerp(b, c.b, ik); ret.a = lerp(a, c.a, ik); return ret; } //-------------------------------------------------------------------- AGG_INLINE void add(const self_type& c, unsigned cover) { calc_type cr, cg, cb, ca; if (cover == cover_mask) { if (c.a == base_mask) { *this = c; return; } else { cr = r + c.r; cg = g + c.g; cb = b + c.b; ca = a + c.a; } } else { cr = r + mult_cover(c.r, cover); cg = g + mult_cover(c.g, cover); cb = b + mult_cover(c.b, cover); ca = a + mult_cover(c.a, cover); } r = (value_type)((cr > calc_type(base_mask)) ? calc_type(base_mask) : cr); g = (value_type)((cg > calc_type(base_mask)) ? calc_type(base_mask) : cg); b = (value_type)((cb > calc_type(base_mask)) ? calc_type(base_mask) : cb); a = (value_type)((ca > calc_type(base_mask)) ? calc_type(base_mask) : ca); } //-------------------------------------------------------------------- template AGG_INLINE void apply_gamma_dir(const GammaLUT& gamma) { r = gamma.dir(r); g = gamma.dir(g); b = gamma.dir(b); } //-------------------------------------------------------------------- template AGG_INLINE void apply_gamma_inv(const GammaLUT& gamma) { r = gamma.inv(r); g = gamma.inv(g); b = gamma.inv(b); } //-------------------------------------------------------------------- static self_type no_color() { return self_type(0,0,0,0); } //-------------------------------------------------------------------- static self_type from_wavelength(double wl, double gamma = 1.0) { return self_type(rgba::from_wavelength(wl, gamma)); } }; //------------------------------------------------------rgba16_gamma_dir template rgba16 rgba16_gamma_dir(rgba16 c, const GammaLUT& gamma) { return rgba16(gamma.dir(c.r), gamma.dir(c.g), gamma.dir(c.b), c.a); } //------------------------------------------------------rgba16_gamma_inv template rgba16 rgba16_gamma_inv(rgba16 c, const GammaLUT& gamma) { return rgba16(gamma.inv(c.r), gamma.inv(c.g), gamma.inv(c.b), c.a); } //====================================================================rgba32 struct rgba32 { typedef float value_type; typedef double calc_type; typedef double long_type; typedef rgba32 self_type; value_type r; value_type g; value_type b; value_type a; //-------------------------------------------------------------------- rgba32() {} //-------------------------------------------------------------------- rgba32(value_type r_, value_type g_, value_type b_, value_type a_= 1) : r(r_), g(g_), b(b_), a(a_) {} //-------------------------------------------------------------------- rgba32(const self_type& c, float a_) : r(c.r), g(c.g), b(c.b), a(a_) {} //-------------------------------------------------------------------- rgba32(const rgba& c) : r(value_type(c.r)), g(value_type(c.g)), b(value_type(c.b)), a(value_type(c.a)) {} //-------------------------------------------------------------------- rgba32(const rgba8& c) : r(value_type(c.r / 255.0)), g(value_type(c.g / 255.0)), b(value_type(c.b / 255.0)), a(value_type(c.a / 255.0)) {} //-------------------------------------------------------------------- rgba32(const srgba8& c) : r(sRGB_conv::rgb_from_sRGB(c.r)), g(sRGB_conv::rgb_from_sRGB(c.g)), b(sRGB_conv::rgb_from_sRGB(c.b)), a(sRGB_conv::alpha_from_sRGB(c.a)) {} //-------------------------------------------------------------------- rgba32(const rgba16& c) : r(value_type(c.r / 65535.0)), g(value_type(c.g / 65535.0)), b(value_type(c.b / 65535.0)), a(value_type(c.a / 65535.0)) {} //-------------------------------------------------------------------- operator rgba() const { return rgba(r, g, b, a); } //-------------------------------------------------------------------- operator rgba8() const { return rgba8( uround(r * 255.0), uround(g * 255.0), uround(b * 255.0), uround(a * 255.0)); } //-------------------------------------------------------------------- operator srgba8() const { return srgba8( sRGB_conv::rgb_to_sRGB(r), sRGB_conv::rgb_to_sRGB(g), sRGB_conv::rgb_to_sRGB(b), sRGB_conv::alpha_to_sRGB(a)); } //-------------------------------------------------------------------- operator rgba16() const { return rgba8( uround(r * 65535.0), uround(g * 65535.0), uround(b * 65535.0), uround(a * 65535.0)); } //-------------------------------------------------------------------- static AGG_INLINE double to_double(value_type a) { return a; } //-------------------------------------------------------------------- static AGG_INLINE value_type from_double(double a) { return value_type(a); } //-------------------------------------------------------------------- static AGG_INLINE value_type empty_value() { return 0; } //-------------------------------------------------------------------- static AGG_INLINE value_type full_value() { return 1; } //-------------------------------------------------------------------- AGG_INLINE bool is_transparent() const { return a <= 0; } //-------------------------------------------------------------------- AGG_INLINE bool is_opaque() const { return a >= 1; } //-------------------------------------------------------------------- static AGG_INLINE value_type invert(value_type x) { return 1 - x; } //-------------------------------------------------------------------- static AGG_INLINE value_type multiply(value_type a, value_type b) { return value_type(a * b); } //-------------------------------------------------------------------- static AGG_INLINE value_type demultiply(value_type a, value_type b) { return (b == 0) ? 0 : value_type(a / b); } //-------------------------------------------------------------------- template static AGG_INLINE T downscale(T a) { return a; } //-------------------------------------------------------------------- template static AGG_INLINE T downshift(T a, unsigned n) { return n > 0 ? a / (1 << n) : a; } //-------------------------------------------------------------------- static AGG_INLINE value_type mult_cover(value_type a, cover_type b) { return value_type(a * b / cover_mask); } //-------------------------------------------------------------------- static AGG_INLINE cover_type scale_cover(cover_type a, value_type b) { return cover_type(uround(a * b)); } //-------------------------------------------------------------------- // Interpolate p to q by a, assuming q is premultiplied by a. static AGG_INLINE value_type prelerp(value_type p, value_type q, value_type a) { return (1 - a) * p + q; // more accurate than "p + q - p * a" } //-------------------------------------------------------------------- // Interpolate p to q by a. static AGG_INLINE value_type lerp(value_type p, value_type q, value_type a) { // The form "p + a * (q - p)" avoids a multiplication, but may produce an // inaccurate result. For example, "p + (q - p)" may not be exactly equal // to q. Therefore, stick to the basic expression, which at least produces // the correct result at either extreme. return (1 - a) * p + a * q; } //-------------------------------------------------------------------- self_type& clear() { r = g = b = a = 0; return *this; } //-------------------------------------------------------------------- self_type& transparent() { a = 0; return *this; } //-------------------------------------------------------------------- AGG_INLINE self_type& opacity(double a_) { if (a_ < 0) a = 0; else if (a_ > 1) a = 1; else a = value_type(a_); return *this; } //-------------------------------------------------------------------- double opacity() const { return a; } //-------------------------------------------------------------------- AGG_INLINE self_type& premultiply() { if (a < 1) { if (a <= 0) { r = g = b = 0; } else { r *= a; g *= a; b *= a; } } return *this; } //-------------------------------------------------------------------- AGG_INLINE self_type& demultiply() { if (a < 1) { if (a <= 0) { r = g = b = 0; } else { r /= a; g /= a; b /= a; } } return *this; } //-------------------------------------------------------------------- AGG_INLINE self_type gradient(const self_type& c, double k) const { self_type ret; ret.r = value_type(r + (c.r - r) * k); ret.g = value_type(g + (c.g - g) * k); ret.b = value_type(b + (c.b - b) * k); ret.a = value_type(a + (c.a - a) * k); return ret; } //-------------------------------------------------------------------- AGG_INLINE void add(const self_type& c, unsigned cover) { if (cover == cover_mask) { if (c.is_opaque()) { *this = c; return; } else { r += c.r; g += c.g; b += c.b; a += c.a; } } else { r += mult_cover(c.r, cover); g += mult_cover(c.g, cover); b += mult_cover(c.b, cover); a += mult_cover(c.a, cover); } if (a > 1) a = 1; if (r > a) r = a; if (g > a) g = a; if (b > a) b = a; } //-------------------------------------------------------------------- template AGG_INLINE void apply_gamma_dir(const GammaLUT& gamma) { r = gamma.dir(r); g = gamma.dir(g); b = gamma.dir(b); } //-------------------------------------------------------------------- template AGG_INLINE void apply_gamma_inv(const GammaLUT& gamma) { r = gamma.inv(r); g = gamma.inv(g); b = gamma.inv(b); } //-------------------------------------------------------------------- static self_type no_color() { return self_type(0,0,0,0); } //-------------------------------------------------------------------- static self_type from_wavelength(double wl, double gamma = 1) { return self_type(rgba::from_wavelength(wl, gamma)); } }; } #endif