#ifndef _imageTypes_h_ #define _imageTypes_h_ // imageTypes.h // // Copyright (c) 2003 Philip Romanik, Amy Muntz // // Permission to use, copy, modify, distribute, and sell this software and // its documentation for any purpose is hereby granted without fee, provided // that (i) the above copyright notices and this permission notice appear in // all copies of the software and related documentation, and (ii) the names // of Philip Romanik and Amy Muntz may not be used in any advertising or // publicity relating to the software without the specific, prior written // permission of Philip Romanik and Amy Muntz. // // Use of this software and/or its documentation will be deemed to be // acceptance of these terms. // // THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, // EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY // WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. // // IN NO EVENT SHALL PHILIP ROMANIK OR AMY MUNTZ BE LIABLE FOR // ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, // OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, // WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF // LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE // OF THIS SOFTWARE. // // // Definition of our supported image types #ifdef WIN32 #pragma warning(disable:4786) // Disable warning regarding long symbol names caused by templates #pragma warning(disable:4244) // Don't show possible conversion error for op+= and related #endif #include "geometry.h" #include // Ruler // 1 2 3 4 5 6 6 //345678901234567890123456789012345678901234567890123456789012345 // ***************** // * * // * apLimitInfo * // * * // ***************** // // A generic image processing function needs to know certain // information about data types. For example, the minimum value // for a type is something which is difficult to decide at runtime. // A apTypeInfo object should be defined for each datatype. This // allows proper rounding, clamping, etc. to occur. // // One object is constructed per type. This object is similar to the // numeric_limits<> defined in , except we only care about // min() and max(). template class apLimitInfo { public: static apLimitInfo sType; T minValue; T maxValue; private: apLimitInfo (T min, T max) : minValue (min), maxValue (max) {} }; // Perform limit checking so pixel values don't wrap // Usage: D dst = apLimit (src); // Use this instead of 'dst= src' // Some specializations are found elsewhere in this file template D apLimit (const S& src) { return src < apLimitInfo::sType.minValue ? apLimitInfo::sType.minValue : (src > apLimitInfo::sType.maxValue ? apLimitInfo::sType.maxValue : static_cast(src)); } // *********************** // * * // * Generic Functions * // * * // *********************** // // The image processing functions frequently call functions // like this to perform basic operations. These do so with // clamping. // // We would very much like to define a generic function template // that looks like, // // template // D add2 (const S1& s1, const S2& s2) // { // return static_cast(s1) + s2; // } // // However, we aren't so fortunate, because we also want to // define a function that looks like this, // // template // D add2 (const apClampedTmpl& s1, const apClampedTmpl& s2) // { // return apLimit (s1.val + s2.val); // d = s1 + s2; // } // // The problem is that these create an ambiguity when a template // of the latter type (ie. apClampedTmpl<>) is instantiated. See // the C++ standard, "Partial Ordering of Function Templates" // (section 5.5.2) and "Template Argument Deduction" (section 8.2). // // The behavior on various compilers is erratic. To avoid this problem, // we explicitly define the common forms we'll need and let the compiler // generate the other ones as needed. This avoid the ambiguity error. #if 0 // early gcc can handle this without problem. For now, we'll define // these by hand. template D add2 (const S1& s1, const S2& s2) { return static_cast(s1) + s2; } template D sub2 (const S1& s1, const S2& s2) { return static_cast(s1) - s2; } template D mul2 (const S1& s1, const S2& s2) { return static_cast(s1) * s2; } template D div2 (const S1& s1, const S2& s2) { return static_cast(s1) / s2; } template D scale (S& s1, float scaling) { return static_cast (scaling * static_cast(s1)); } #else // We define manual versions. If you encounter errors with missing versions // of add2<>, sub2<>, mul2<>, div2<>, or scale<>, chances are you will need // to add more entries here. template D add2 (Pel32s s1, Pel32s s2) { return static_cast(s1 + s2);} template D sub2 (Pel32s s1, Pel32s s2) { return static_cast(s1 - s2);} template D mul2 (Pel32s s1, Pel32s s2) { return static_cast(s1 * s2);} template D div2 (Pel32s s1, Pel32s s2) { return static_cast(s1 / s2);} template D scale (Pel32s s1, float scaling) { return static_cast (scaling * s1);} template D scale (Pel8 s1, float scaling) { return static_cast (scaling * s1);} template D scale (Pel16 s1, float scaling) { return static_cast (scaling * s1);} template D scale (double s1, float scaling) { return static_cast (scaling * s1);} #endif // ************************ // * * // * Clamped pixel type * // * * // ************************ // // Built-in types, like char or short do not have // any overflow checks built-in. For example: // char c = 250; // c += 10; // In this case, c has a value of 4. This wrapping will // cause problems for image processing routines so we // want something like this: // apClampedTmpl c = 250; // c += 10; // And in this case, c = 255. This doesn't come without // doing a bit of work, but it is worth it. // // This list of supported operators is not necessarily // complete, but it is sufficient for our class right now. // // Everything is defined in the header file. The definitions are simple // and win32 (at least VC6) has problems when you split the declaration // from the definition (and use the 'export' keyword). template class apClampedTmpl { public: T val; apClampedTmpl () : val(0) {} apClampedTmpl (T v) : val(v) {} operator T () const { return val;} apClampedTmpl (const apClampedTmpl& src) { val = apLimit (src.val); } template apClampedTmpl (const apClampedTmpl& src) { val = apLimit (src.val); } apClampedTmpl& operator= (const apClampedTmpl& src) { val = apLimit (src.val); return *this; } /* template apClampedTmpl& operator= (const apClampedTmpl& src) { val = apLimit (src.val); return *this; } */ /* apClampedTmpl& operator= (const T& c) { val = c; return *this; } */ apClampedTmpl& operator+= (const apClampedTmpl& s) { val = apLimit (val + s.val); return *this; } apClampedTmpl& operator-= (const apClampedTmpl& s) { val = apLimit (val - s.val); return *this; } apClampedTmpl& operator*= (const apClampedTmpl& s) { val = apLimit (val * s.val); return *this; } apClampedTmpl& operator/= (const apClampedTmpl& s) { val = apLimit (val / s.val); return *this; } template apClampedTmpl operator+ (const apClampedTmpl& s2) { apClampedTmpl dst; dst.val = apLimit (val + s2.val); return dst; } }; // ************* // * * // * typedef * // * * // ************* // // Common types our applications might use typedef apClampedTmpl apClampedPel8; typedef apClampedTmpl apClampedPel16; typedef apClampedTmpl apClampedPel32; typedef apClampedTmpl apClampedPel32s; // This is signed!! // ********************************* // * * // * apClampedTmpl non-member * // * * // ********************************* // // bool operator== (const apClampedTmpl& s1, const apClampedTmpl& s2); // bool operator== (const apClampedTmpl& s1, const T& s2); // bool operator!= (const apClampedTmpl& s1, const apClampedTmpl& s2); // bool operator!= (const apClampedTmpl& s1, const T& s2); // // apClampedTmpl operator+ (const apClampedTmpl& s1, const T2& s2); // apClampedTmpl operator- (const apClampedTmpl& s1, const T2& s2); // apClampedTmpl operator* (const apClampedTmpl& s1, const T2& s2); // apClampedTmpl operator/ (const apClampedTmpl& s1, const T2& s2); // // std::ostream& operator<< (std::ostream& stream, const apClampedTmpl& val); template bool operator== (const apClampedTmpl& s1, const apClampedTmpl& s2) { return s1.val == s2.val; } template bool operator!= (const apClampedTmpl& s1, const apClampedTmpl& s2) { return s1.val != s2.val; } template bool operator== (const apClampedTmpl& s1, const T& s2) { return s1.val == s2; } template bool operator!= (const apClampedTmpl& s1, const T& s2) { return s1.val != s2; } template apClampedTmpl operator+ (const apClampedTmpl& s1, const T2& s2) { apClampedTmpl dst; dst.val = apLimit (s1.val + s2); return dst; } template apClampedTmpl operator- (const apClampedTmpl& s1, const T2& s2) { apClampedTmpl dst; dst.val = apLimit (s1.val - s2); return dst; } template apClampedTmpl operator* (const apClampedTmpl& s1, const T2& s2) { apClampedTmpl dst; dst.val = apLimit (s1.val * s2); return dst; } template apClampedTmpl operator/ (const apClampedTmpl& s1, const T2& s2) { apClampedTmpl dst; dst.val = apLimit (s1.val / s2); return dst; } template std::ostream& operator<< (std::ostream& stream, const apClampedTmpl& val) { stream << val.val; return stream; } template<> std::ostream& operator<< (std::ostream& stream, const apClampedTmpl& rgb); // We want to display the pixel value as a number, not as a character // Even though this is an unsigned quantity, some compilers make mistakes // **************************** // * * // * apClampedTmpl mixed * // * * // **************************** // // add2 (const apClampedTmpl& s1, const apClampedTmpl& s2); // sub2 (const apClampedTmpl& s1, const apClampedTmpl& s2); // mul2 (const apClampedTmpl& s1, const apClampedTmpl& s2); // div2 (const apClampedTmpl& s1, const apClampedTmpl& s2); // scale (const apClampedTmpl& s1, float scaling); // // apClampedTmpl operator+ (const apClampedTmpl& s1, const apClampedTmpl& s2); // apClampedTmpl operator- (const apClampedTmpl& s1, const apClampedTmpl& s2); // apClampedTmpl operator* (const apClampedTmpl& s1, const apClampedTmpl& s2); // apClampedTmpl operator/ (const apClampedTmpl& s1, const apClampedTmpl& s2); // // apClampedTmpl& operator+= (apClampedTmpl& s1, const apClampedTmpl& s2); // apClampedTmpl& operator-= (apClampedTmpl& s1, const apClampedTmpl& s2); // apClampedTmpl& operator*= (apClampedTmpl& s1, const apClampedTmpl& s2); // apClampedTmpl& operator/= (apClampedTmpl& s1, const apClampedTmpl& s2); // apLimit<> specializations template<> apClampedPel8 apLimit (const apClampedPel32s& src); template<> Pel8 apLimit (const Pel32s& src); template<> Pel8 apLimit (const Pel8& src); template<> Pel32s apLimit (const Pel32s& src); template<> Pel32s apLimit (const Pel32& src); template<> apClampedPel8 apLimit (const apClampedPel8& src); template<> apClampedPel32s apLimit (const apClampedPel32s& src); template D add2 (const apClampedTmpl& s1, const apClampedTmpl& s2) { return apLimit (s1.val + s2.val); // d = s1 + s2; } template D sub2 (const apClampedTmpl& s1, const apClampedTmpl& s2) { return apLimit (s1.val - s2.val); // d = s1 - s2; } template D mul2 (const apClampedTmpl& s1, const apClampedTmpl& s2) { return apLimit (s1.val * s2.val); // d = s1 * s2; } template D div2 (const apClampedTmpl& s1, const apClampedTmpl& s2) { return apLimit (s1.val / s2.val); // d = s1 / s2; } template D scale (const apClampedTmpl& s1, float scaling) { return apLimit (scaling * static_cast(s1.val)); // d = k1 * s1 } template apClampedTmpl operator+ (const apClampedTmpl& s1, const apClampedTmpl& s2) { apClampedTmpl dst = add2, apClampedTmpl, apClampedTmpl > (s1, s2); return dst; } template apClampedTmpl operator- (const apClampedTmpl& s1, const apClampedTmpl& s2) { apClampedTmpl dst = sub2, apClampedTmpl, apClampedTmpl > (s1, s2); return dst; } template apClampedTmpl operator* (const apClampedTmpl& s1, const apClampedTmpl& s2) { apClampedTmpl dst = mul2, apClampedTmpl, apClampedTmpl > (s1, s2); return dst; } template apClampedTmpl operator/ (const apClampedTmpl& s1, const apClampedTmpl& s2) { apClampedTmpl dst = div2, apClampedTmpl, apClampedTmpl > (s1, s2); return dst; } template apClampedTmpl& operator+= (apClampedTmpl& s1, const apClampedTmpl& s2) { s1.val = apLimit (s1.val + s2.val); // d = s1 + s2; return s1; } template apClampedTmpl& operator-= (apClampedTmpl& s1, const apClampedTmpl& s2) { s1.val = apLimit (s1.val - s2.val); // d = s1 - s2; return s1; } template apClampedTmpl& operator*= (apClampedTmpl& s1, const apClampedTmpl& s2) { s1.val = apLimit (s1.val * s2.val); // d = s1 * s2; return s1; } template apClampedTmpl& operator/= (apClampedTmpl& s1, const apClampedTmpl& s2) { s1.val = apLimit (s1.val / s2.val); // d = s1 / s2; return s1; } // ****************** // * * // * apRGBTmpl * // * * // ****************** // // A generic rgb triplet. I originally wanted to name it RGB but // this runs into problems with macros defined on some platforms // (like win32). The data members are left public so it acts like // a struct. There are no virtual functions so the size // of this object matches how we want memory to be written in // memory. Most operators are written as non-member functions. template class apRGBTmpl { public: T red; T green; T blue; apRGBTmpl () : red(0), green(0), blue(0) {} explicit apRGBTmpl (T v) : red(v), green(v), blue(v) {} apRGBTmpl (T r, T g, T b) : red(r), green(g), blue(b) {} T ts1 () const { return red;} T ts2 () const { return green;} T ts3 () const { return blue;} // Treat as generic tristimulas values apRGBTmpl (const apRGBTmpl& s) { red = s.red; green = s.green; blue = s.blue; } // Copy constructor apRGBTmpl& operator= (const apRGBTmpl& src) { red = src.red; green = src.green; blue = src.blue; return *this; } template apRGBTmpl (const apRGBTmpl& s) { red = apLimit (s.red); green = apLimit (s.green); blue = apLimit (s.blue); } // Copy constructor template apRGBTmpl& operator= (const apRGBTmpl& src) { red = apLimit (src.red); green = apLimit (src.green); blue = apLimit (src.blue); return *this; } apRGBTmpl& operator= (const T& c) { red = c; green = c; blue = c; return *this; } operator T () const { return (red + green + blue) / 3;} // Conversion to monochrome (averaging) // If you want the NTSC definition, use this // double v = .299 * red + .587 * green + .114 * blue; // return static_cast (v); // A conversion operator is not needed because it does // the same job as this: // template apRGBTmpl (const apRGBTmpl& s) // // Most compilers we tested did not complain about it. // // template operator apRGBTmpl () const // { // apRGBTmpl dst; // dst.red = apLimit (red); // dst.green = apLimit (green); // dst.blue = apLimit (blue); // return dst; // } apRGBTmpl& operator+= (const apRGBTmpl& s) { red = apLimit (red + s.red); green = apLimit (green + s.green); blue = apLimit (blue + s.blue); return *this; } apRGBTmpl& operator-= (const apRGBTmpl& s) { red = apLimit (red - s.red); green = apLimit (green - s.green); blue = apLimit (blue - s.blue); return *this; } apRGBTmpl& operator*= (const apRGBTmpl& s) { red = apLimit (red * s.red); green = apLimit (green * s.green); blue = apLimit (blue * s.blue); return *this; } apRGBTmpl& operator/= (const apRGBTmpl& s) { red = apLimit (red / s.red); green = apLimit (green / s.green); blue = apLimit (blue / s.blue); return *this; } apRGBTmpl& operator+= (const T& s) { red = apLimit (red + s); green = apLimit (green + s); blue = apLimit (blue + s); return *this; } apRGBTmpl& operator-= (const T& s) { red = apLimit (red - s); green = apLimit (green - s); blue = apLimit (blue - s); return *this; } apRGBTmpl& operator*= (const T& s) { red = apLimit (red * s); green = apLimit (green * s); blue = apLimit (blue * s); return *this; } apRGBTmpl& operator/= (const T& s) { red = apLimit (red / s); green = apLimit (green / s); blue = apLimit (blue / s); return *this; } }; // ************* // * * // * typedef * // * * // ************* typedef apRGBTmpl apRGB; typedef apRGBTmpl apRGBPel32; typedef apRGBTmpl apRGBPel32s; // This is signed!! // ***************************** // * * // * apRGBTmpl non-member * // * * // ***************************** // // operator== (const apRGBTmpl& s1, const apRGBTmpl& s2) // operator!= (const apRGBTmpl& s1, const apRGBTmpl& s2) // // operator+ (const apRGBTmpl& s1, const T2& s2) // operator- (const apRGBTmpl& s1, const T2& s2) // operator* (const apRGBTmpl& s1, const T2& s2) // operator/ (const apRGBTmpl& s1, const T2& s2) // // operator+ (const apRGBTmpl& s1, const double& s2) // operator- (const apRGBTmpl& s1, const double& s2) // operator* (const apRGBTmpl& s1, const double& s2) // operator/ (const apRGBTmpl& s1, const double& s2) // operator<< (std::ostream& stream, const apRGBTmpl& rgb) template bool operator== (const apRGBTmpl& s1, const apRGBTmpl& s2) { return (s1.red == s2.red) && (s1.green == s2.green) && (s1.blue == s2.blue); } template bool operator!= (const apRGBTmpl& s1, const apRGBTmpl& s2) { return (s1.red != s2.red) || (s1.green != s2.green) || (s1.blue != s2.blue); } template apRGBTmpl operator+ (const apRGBTmpl& s1, const T2& s2) { apRGBTmpl dst; dst.red = apLimit (s1.red + s2); dst.green = apLimit (s1.green + s2); dst.blue = apLimit (s1.blue + s2); return dst; } // template void apLimit (const S& src, D& dst) template apRGBTmpl operator- (const apRGBTmpl& s1, const T2& s2) { apRGBTmpl dst; dst.red = apLimit (s1.red - s2); dst.green = apLimit (s1.green - s2); dst.blue = apLimit (s1.blue - s2); return dst; } template apRGBTmpl operator* (const apRGBTmpl& s1, const T2& s2) { apRGBTmpl dst; dst.red = apLimit (s1.red * s2); dst.green = apLimit (s1.green * s2); dst.blue = apLimit (s1.blue * s2); return dst; } template apRGBTmpl operator/ (const apRGBTmpl& s1, const T2& s2) { apRGBTmpl dst; dst.red = apLimit (s1.red / s2); dst.green = apLimit (s1.green / s2); dst.blue = apLimit (s1.blue / s2); return dst; } template apRGBTmpl operator+ (const apRGBTmpl& s1, const double& d) { apRGBTmpl dst; dst.red = apLimit (d + s1.red); dst.green = apLimit (d + s1.green); dst.blue = apLimit (d + s1.blue); return dst; } template apRGBTmpl operator- (const apRGBTmpl& s1, const double& d) { apRGBTmpl dst; dst.red = apLimit (d - s1.red); dst.green = apLimit (d - s1.green); dst.blue = apLimit (d - s1.blue); return dst; } template apRGBTmpl operator* (const apRGBTmpl& s1, const double& d) { apRGBTmpl dst; dst.red = apLimit (d * s1.red); dst.green = apLimit (d * s1.green); dst.blue = apLimit (d * s1.blue); return dst; } template apRGBTmpl operator/ (const apRGBTmpl& s1, const double& d) { apRGBTmpl dst; dst.red = apLimit (d / s1.red); dst.green = apLimit (d / s1.green); dst.blue = apLimit (d / s1.blue); return dst; } template std::ostream& operator<< (std::ostream& stream, const apRGBTmpl& rgb) { stream << "(" << rgb.red << "," << rgb.green << "," << rgb.blue << ")"; return stream; } template<> std::ostream& operator<< (std::ostream& stream, const apRGBTmpl& rgb); // We want to display the pixel value as a number, not as a character // Even though this is an unsigned quantity, some compilers make mistakes // ************************ // * * // * apRGBTmpl mixed * // * * // ************************ // // apLimit (const RGBPel32s& src, RGB& dst); // // 3 parameter operations // add2 (const apRGBTmpl& s1, const apRGBTmpl& s2) // sub2 (const apRGBTmpl& s1, const apRGBTmpl& s2) // mul2 (const apRGBTmpl& s1, const apRGBTmpl& s2) // div2 (const apRGBTmpl& s1, const apRGBTmpl& s2) // // operator+ (const apRGBTmpl& s1, const apRGBTmpl& s2) // operator- (const apRGBTmpl& s1, const apRGBTmpl& s2) // operator* (const apRGBTmpl& s1, const apRGBTmpl& s2) // operator/ (const apRGBTmpl& s1, const apRGBTmpl& s2) // // operator+= (const apRGBTmpl& s1, const apRGBTmpl& s2) // operator-= (const apRGBTmpl& s1, const apRGBTmpl& s2) // operator*= (const apRGBTmpl& s1, const apRGBTmpl& s2) // operator/= (const apRGBTmpl& s1, const apRGBTmpl& s2) // template<> apRGB apLimit (const apRGBPel32s& src); template<> apRGB apLimit (const apRGB& src); template<> apRGBPel32s apLimit (const apRGBPel32s& src); template apRGBTmpl apMin (const apRGBTmpl& s1, const apRGBTmpl& s2) { apRGBTmpl d; d.red = apLimit (apMin (s1.red, s2.red)); d.green = apLimit (apMin (s1.green, s2.green)); d.blue = apLimit (apMin (s1.blue, s2.blue)); return d; } template apRGBTmpl add2 (const apRGBTmpl& s1, const apRGBTmpl& s2) { apRGBTmpl d; d.red = apLimit (s1.red + s2.red); d.green = apLimit (s1.green + s2.green); d.blue = apLimit (s1.blue + s2.blue); return d; } template apRGBTmpl sub2 (const apRGBTmpl& s1, const apRGBTmpl& s2) { apRGBTmpl d; d.red = apLimit (s1.red - s2.red); d.green = apLimit (s1.green - s2.green); d.blue = apLimit (s1.blue - s2.blue); return d; } template apRGBTmpl mul2 (const apRGBTmpl& s1, const apRGBTmpl& s2) { apRGBTmpl d; d.red = apLimit (s1.red * s2.red); d.green = apLimit (s1.green * s2.green); d.blue = apLimit (s1.blue * s2.blue); return d; } template apRGBTmpl div2 (const apRGBTmpl& s1, const apRGBTmpl& s2) { apRGBTmpl d; d.red = apLimit (s1.red / s2.red); d.green = apLimit (s1.green / s2.green); d.blue = apLimit (s1.blue / s2.blue); return d; } template apRGBTmpl scale (const apRGBTmpl& s1, float scaling) { apRGBTmpl d; d.red = apLimit (scaling * static_cast(s1.red)); d.green = apLimit (scaling * static_cast(s1.green)); d.blue = apLimit (scaling * static_cast(s1.blue)); return d; } template apRGBTmpl operator+ (const apRGBTmpl& s1, const apRGBTmpl& s2) { apRGBTmpl d; d.red = apLimit (s1.red + s2.red); d.green = apLimit (s1.green + s2.green); d.blue = apLimit (s1.blue + s2.blue); return d; // On many platforms, a single line can be used, but some versions of // gcc have trouble figuring out which version of add2<> to use. // return add2 > (s1, s2); } template apRGBTmpl operator- (const apRGBTmpl& s1, const apRGBTmpl& s2) { apRGBTmpl d; d.red = apLimit (s1.red - s2.red); d.green = apLimit (s1.green - s2.green); d.blue = apLimit (s1.blue - s2.blue); return d; // See comment under operator+ (above) //return sub2 > (s1, s2); } template apRGBTmpl operator* (const apRGBTmpl& s1, const apRGBTmpl& s2) { apRGBTmpl d; d.red = apLimit (s1.red * s2.red); d.green = apLimit (s1.green * s2.green); d.blue = apLimit (s1.blue * s2.blue); return d; // See comment under operator+ (above) // return mul2 > (s1, s2); } template apRGBTmpl operator/ (const apRGBTmpl& s1, const apRGBTmpl& s2) { apRGBTmpl d; d.red = apLimit (s1.red / s2.red); d.green = apLimit (s1.green / s2.green); d.blue = apLimit (s1.blue / s2.blue); return d; // See comment under operator+ (above) // return div2 > (s1, s2); } template apRGBTmpl& operator+= (apRGBTmpl& s1, const apRGBTmpl& s2) { s1.red = apLimit (s1.red + s2.red); s1.green = apLimit (s1.green + s2.green); s1.blue = apLimit (s1.blue + s2.blue); return s1; } template apRGBTmpl& operator-= (apRGBTmpl& s1, const apRGBTmpl& s2) { s1.red = apLimit (s1.red - s2.red); s1.green = apLimit (s1.green - s2.green); s1.blue = apLimit (s1.blue - s2.blue); return s1; } template apRGBTmpl& operator*= (apRGBTmpl& s1, const apRGBTmpl& s2) { s1.red = apLimit (s1.red * s2.red); s1.green = apLimit (s1.green * s2.green); s1.blue = apLimit (s1.blue * s2.blue); return s1; } template apRGBTmpl& operator/= (apRGBTmpl& s1, const apRGBTmpl& s2) { s1.red = apLimit (s1.red / s2.red); s1.green = apLimit (s1.green / s2.green); s1.blue = apLimit (s1.blue / s2.blue); return s1; } #endif // _imageTypes_h_