diff options
Diffstat (limited to 'lib/kopainter/koColor.cpp')
-rw-r--r-- | lib/kopainter/koColor.cpp | 742 |
1 files changed, 742 insertions, 0 deletions
diff --git a/lib/kopainter/koColor.cpp b/lib/kopainter/koColor.cpp new file mode 100644 index 000000000..813aee8dc --- /dev/null +++ b/lib/kopainter/koColor.cpp @@ -0,0 +1,742 @@ +/* This file is part of the KDE project + Copyright (c) 1999 Matthias Elter (me@kde.org) + Copyright (c) 2001-2002 Igor Jansen (rm@kde.org) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include "koColor.h" +#include "kdebug.h" +#include <cmath> + +KoColor::KoColor() +{ + // initialise to black + mNative = csRGB; + // RGB + mR = 0; + mG = 0; + mB = 0; + // HSV + mH = mV = 0; + mS = 100; + // CMYK + mC = 0; + mY = 0; + mM = 0; + mK = 0; + // Lab + mL = 0; + ma = 0; + mB = 0; + rgbChanged(); +} + +KoColor::KoColor(int a, int b, int c, cSpace m) +{ + switch(m) + { + case csRGB: + mR = a; + mG = b; + mB = c; + mNative = csRGB; + rgbChanged(); + break; + case csHSV: + mH = a; + mS = b; + mV = c; + mNative = csHSV; + hsvChanged(); + break; + case csLab: + mL = a; + ma = b; + mB = c; + mNative = csLab; + labChanged(); + break; + default: + mR = 0; + mG = 0; + mB = 0; + mNative = csRGB; + rgbChanged(); + } +} + +KoColor::KoColor(int c, int m, int y, int k) +{ + mC = c; + mM = m; + mY = y; + mK = k; + mNative = csCMYK; + cmykChanged(); +} + +KoColor::KoColor(const TQColor &c) +{ + mR = c.red(); + mG = c.green(); + mB = c.blue(); + mNative = csRGB; + rgbChanged(); +} + +KoColor::KoColor(const TQString &name) +{ + setNamedColor(name); +} + +int KoColor::R() const +{ + if(!mRGBvalid) + calcRGB(); + return mR; +} + +int KoColor::G() const +{ + if(!mRGBvalid) + calcRGB(); + return mG; +} + +int KoColor::B() const +{ + if(!mRGBvalid) + calcRGB(); + return mB; +} + +int KoColor::H() const +{ + if(!mHSVvalid) + calcHSV(); + return mH; +} + +int KoColor::S() const +{ + if(!mHSVvalid) + calcHSV(); + return mS; +} + +int KoColor::V() const +{ + if(!mHSVvalid) + calcHSV(); + return mV; +} + +int KoColor::C() const +{ + if(!mCMYKvalid) + calcCMYK(); + return mC; +} + +int KoColor::M() const +{ + if(!mCMYKvalid) + calcCMYK(); + return mM; +} + +int KoColor::Y() const +{ + if(!mCMYKvalid) + calcCMYK(); + return mY; +} + +int KoColor::K() const +{ + if(!mCMYKvalid) + calcCMYK(); + return mK; +} + +int KoColor::L() const +{ + if(!mLABvalid) + calcLAB(); + return mL; +} + +int KoColor::a() const +{ + if(!mLABvalid) + calcLAB(); + return ma; +} + +int KoColor::b() const +{ + if(!mLABvalid) + calcLAB(); + return mB; +} + +void KoColor::rgb(int *R, int *G, int *B) const +{ + if(!mRGBvalid) + calcRGB(); + *R = mR; + *G = mG; + *B = mB; +} + +void KoColor::hsv(int *H, int *S, int *V) const +{ + if(!mHSVvalid) + calcHSV(); + *H = mH; + *S = mS; + *V = mV; +} + +void KoColor::lab(int *L, int *a, int *b) const +{ + if(!mLABvalid) + calcLAB(); + *L = mL; + *a = ma; + *b = mB; +} + +void KoColor::cmyk(int *C, int *M, int *Y, int *K) const +{ + if(!mCMYKvalid) + calcCMYK(); + *C = mC; + *M = mM; + *Y = mY; + *K = mK; +} + +TQString KoColor::name() const +{ + TQString s; + switch(mNative) + { + case csRGB: + s.sprintf("#%02x%02x%02x", R(), G(), B()); + break; + case csHSV: + s.sprintf("$%02x%02x%02x", H(), S(), V()); + break; + case csCMYK: + s.sprintf("@%02x%02x%02x%02x", C(), M(), Y(), K()); + break; + case csLab: + s.sprintf("*%02x%02x%02x", L(), a(), b()); + break; + default: + s.sprintf("#%02x%02x%02x", R(), G(), B()); + } + return s; +} + +TQColor KoColor::color() const +{ + if(!mRGBvalid) + calcRGB(); + return TQColor(mR, mG, mB); +} + +void KoColor::setRGB(int R, int G, int B) +{ + mR = R; + mG = G; + mB = B; + mNative = csRGB; + rgbChanged(); +} + +void KoColor::setHSV(int H, int S, int V) +{ + mH = H; + mS = S; + mV = V; + mNative = csHSV; + hsvChanged(); +} + +void KoColor::setLab(int L, int a, int b) +{ + mL = L; + ma = a; + mB = b; + mNative = csLab; + labChanged(); +} + +void KoColor::setCMYK(int C, int M, int Y, int K) +{ + mC = C; + mM = M; + mY = Y; + mK = K; + mNative = csCMYK; + cmykChanged(); +} + +void KoColor::setNamedColor(const TQString &name) +{ + switch(name[0]) + { + case '#': + mR = (hex2int(name[1]) << 4) + hex2int(name[2]); + mG = (hex2int(name[3]) << 4) + hex2int(name[4]); + mB = (hex2int(name[5]) << 4) + hex2int(name[6]); + mNative = csRGB; + rgbChanged(); + break; + case '$': + mH = (hex2int(name[1]) << 4) + hex2int(name[2]); + mS = (hex2int(name[3]) << 4) + hex2int(name[4]); + mV = (hex2int(name[5]) << 4) + hex2int(name[6]); + mNative = csHSV; + hsvChanged(); + break; + case '@': + mC = (hex2int(name[1]) << 4) + hex2int(name[2]); + mM = (hex2int(name[3]) << 4) + hex2int(name[4]); + mY = (hex2int(name[5]) << 4) + hex2int(name[6]); + mK = (hex2int(name[7]) << 4) + hex2int(name[8]); + mNative = csCMYK; + cmykChanged(); + break; + case '*': + mL = (hex2int(name[1]) << 4) + hex2int(name[2]); + ma = (hex2int(name[3]) << 4) + hex2int(name[4]); + mb = (hex2int(name[5]) << 4) + hex2int(name[6]); + mNative = csLab; + labChanged(); + break; + default: + mR = 0; + mG = 0; + mB = 0; + mNative = csRGB; + rgbChanged(); + } +} + +void KoColor::setColor(const TQColor &c) +{ + mR = c.red(); + mG = c.green(); + mB = c.blue(); + mNative = csRGB; + rgbChanged(); +} + +void KoColor::RGBtoHSV(int R, int G, int B, int *H, int *S, int *V) +{ + unsigned int max = R; + unsigned int min = R; + unsigned char maxValue = 0; // r = 0, g = 1, b = 2 + + // find maximum and minimum RGB values + if(static_cast<unsigned int>(G) > max) + { + max = G; + maxValue = 1; + } + if(static_cast<unsigned int>(B) > max) + { + max = B; + maxValue = 2; + } + + if(static_cast<unsigned int>(G) < min) + min = G; + if(static_cast<unsigned int>(B) < min ) + min = B; + + int delta = max - min; + *V = max; // value + *S = max ? (510 * delta + max) / ( 2 * max) : 0; // saturation + + // calc hue + if(*S == 0) + *H = -1; // undefined hue + else + { + switch(maxValue) + { + case 0: // red + if(G >= B) + *H = (120 * (G - B) + delta) / (2 * delta); + else + *H = (120 * (G - B + delta) + delta) / (2 * delta) + 300; + break; + case 1: // green + if(B > R) + *H = 120 + (120 * (B - R) + delta) / (2 * delta); + else + *H = 60 + (120 * (B - R + delta) + delta) / (2 * delta); + break; + case 2: // blue + if(R > G) + *H = 240 + (120 * (R - G) + delta) / (2 * delta); + else + *H = 180 + (120 * (R - G + delta) + delta) / (2 * delta); + break; + } + } +} + +void KoColor::RGBtoLAB(int R, int G, int B, int *L, int *a, int *b) +{ + // Convert between RGB and CIE-Lab color spaces + // Uses ITU-R recommendation BT.709 with D65 as reference white. + // algorithm contributed by "Mark A. Ruzon" <ruzon@CS.Stanford.EDU> + + double X, Y, Z, fX, fY, fZ; + + X = 0.412453 * R + 0.357580 * G + 0.180423 * B; + Y = 0.212671 * R + 0.715160 * G + 0.072169 * B; + Z = 0.019334 * R + 0.119193 * G + 0.950227 * B; + + X /= (255 * 0.950456); + Y /= 255; + Z /= (255 * 1.088754); + + if(Y > 0.008856) + { + fY = pow(Y, 1.0 / 3.0); + *L = static_cast<int>(116.0 * fY - 16.0 + 0.5); + } + else + { + fY = 7.787 * Y + 16.0 / 116.0; + *L = static_cast<int>(903.3 * Y + 0.5); + } + + if(X > 0.008856) + fX = pow(X, 1.0 / 3.0); + else + fX = 7.787 * X + 16.0 / 116.0; + + if(Z > 0.008856) + fZ = pow(Z, 1.0 / 3.0); + else + fZ = 7.787 * Z + 16.0 / 116.0; + + *a = static_cast<int>(500.0 * (fX - fY) + 0.5); + *b = static_cast<int>(200.0 * (fY - fZ) + 0.5); +} + +void KoColor::RGBtoCMYK(int R, int G, int B, int *C, int *M, int *Y, int *K) +{ + // XXX: these algorithms aren't the best. See www.littlecms.com + // for a suitable library, or the posting by Leo Rosenthol for + // a better, but slower algorithm at + // http://lists.kde.org/?l=koffice-devel&m=106698241227054&w=2 + + *C = 255 - R; + *M = 255 - G; + *Y = 255 - B; + + int min = (*C < *M) ? *C : *M; + *K = (min < *Y) ? min : *Y; + + *C -= *K; + *M -= *K; + *Y -= *K; + +} + + +void KoColor::HSVtoRGB(int H, int S, int V, int *R, int *G, int *B) +{ + *R = *G = *B = V; + + if(S != 0 && H != -1) // chromatic + { + if(H >= 360) // angle > 360 + H %= 360; + + unsigned int f = H % 60; + H /= 60; + unsigned int p = static_cast<unsigned int>(2*V*(255-S)+255)/510; + unsigned int q, t; + + if(H & 1) + { + q = static_cast<unsigned int>(2 * V * (15300 - S * f) + 15300) / 30600; + switch(H) + { + case 1: + *R = static_cast<int>(q); + *G = static_cast<int>(V); + *B = static_cast<int>(p); + break; + case 3: + *R = static_cast<int>(p); + *G = static_cast<int>(q); + *B = static_cast<int>(V); + break; + case 5: + *R = static_cast<int>(V); + *G = static_cast<int>(p); + *B = static_cast<int>(q); + break; + } + } + else + { + t = static_cast<unsigned int>(2 * V * (15300 - (S * (60 - f))) + 15300) / 30600; + switch(H) + { + case 0: + *R = static_cast<int>(V); + *G = static_cast<int>(t); + *B = static_cast<int>(p); + break; + case 2: + *R = static_cast<int>(p); + *G = static_cast<int>(V); + *B = static_cast<int>(t); + break; + case 4: + *R = static_cast<int>(t); + *G = static_cast<int>(p); + *B = static_cast<int>(V); + break; + } + } + } +} + +void KoColor::HSVtoLAB(int H, int S, int V, int *L, int *a, int *b) +{ + int R, G, B; + HSVtoRGB(H, S, V, &R, &G, &B); + RGBtoLAB(R, G, B, L, a, b); +} + +void KoColor::HSVtoCMYK(int H, int S, int V, int *C, int *M, int *Y, int*K) +{ + int R, G, B; + HSVtoRGB(H, S, V, &R, &G, &B); + RGBtoCMYK(R, G, B, C, M, Y, K); +} + +void KoColor::LABtoRGB(int L, int a, int b, int *R, int *G, int *B) +{ + // Convert between RGB and CIE-Lab color spaces + // Uses ITU-R recommendation BT.709 with D65 as reference white. + // algorithm contributed by "Mark A. Ruzon" <ruzon@CS.Stanford.EDU> + + double X, Y, Z, fX, fY, fZ; + int RR, GG, BB; + + fY = pow((L + 16.0) / 116.0, 3.0); + if(fY < 0.008856) + fY = L / 903.3; + Y = fY; + + if(fY > 0.008856) + fY = pow(fY, 1.0 / 3.0); + else + fY = 7.787 * fY + 16.0 / 116.0; + + fX = a / 500.0 + fY; + if(fX > 0.206893) + X = pow(fX, 3.0); + else + X = (fX - 16.0 / 116.0) / 7.787; + + fZ = fY - b / 200.0; + if(fZ > 0.206893) + Z = pow(fZ, 3.0); + else + Z = (fZ - 16.0/116.0) / 7.787; + + X *= 0.950456 * 255; + Y *= 255; + Z *= 1.088754 * 255; + + RR = static_cast<int>(3.240479 * X - 1.537150 * Y - 0.498535 * Z + 0.5); + GG = static_cast<int>(-0.969256 * X + 1.875992 * Y + 0.041556 * Z + 0.5); + BB = static_cast<int>(0.055648 * X - 0.204043 * Y + 1.057311 * Z + 0.5); + + *R = RR < 0 ? 0 : RR > 255 ? 255 : RR; + *G = GG < 0 ? 0 : GG > 255 ? 255 : GG; + *B = BB < 0 ? 0 : BB > 255 ? 255 : BB; +} + +void KoColor::LABtoHSV(int L, int a, int b, int *H, int *S, int *V) +{ + int R, G, B; + LABtoRGB(L, a, b, &R, &G, &B); + RGBtoHSV(R, G, B, H, S, V); +} + +void KoColor::LABtoCMYK(int L, int a, int b, int *C, int *M, int *Y, int*K) +{ + int R, G, B; + LABtoRGB(L, a, b, &R, &G, &B); + RGBtoCMYK(R, G, B, C, M, Y, K); +} + +void KoColor::CMYKtoRGB(int C, int M, int Y, int K, int *R, int *G, int *B) +{ + *R = 255 - (C + K); + *G = 255 - (M + K); + *B = 255 - (Y + K); +} + +void KoColor::CMYKtoHSV(int C, int M, int Y, int K, int *H, int *S, int *V) +{ + int R, G, B; + CMYKtoRGB(C, M, Y, K, &R, &G, &B); + RGBtoHSV(R, G, B, H, S, V); +} + +void KoColor::CMYKtoLAB(int C, int M, int Y, int K, int *L, int *a, int *b) +{ + int R, G, B; + CMYKtoRGB(C, M, Y, K, &R, &G, &B); + RGBtoLAB(R, G, B, L, a, b); +} + +int KoColor::hex2int(TQChar c) +{ + if(c.isDigit()) + return c.digitValue(); + else if('A' <= (int)c && (int)c <= 'F') + return c - 'A' + 10; + else if('a' <= (int)c && (int)c <= 'f') + return c - 'a' + 10; + else + return 0; +} + +void KoColor::calcRGB() const +{ + switch(mNative) + { + case csHSV: + HSVtoRGB(mH, mS, mV, &mR, &mG, &mB); + break; + case csLab: + LABtoRGB(mL, ma, mB, &mR, &mG, &mB); + break; + case csCMYK: + CMYKtoRGB(mC, mM, mY, mK, &mR, &mG, &mB); + break; + default: + break; + } + mRGBvalid = true; +} + +void KoColor::calcHSV() const +{ + switch(mNative) + { + case csRGB: + RGBtoHSV(mR, mG, mB, &mH, &mS, &mV); + break; + case csLab: + LABtoHSV(mL, ma, mB, &mH, &mS, &mV); + break; + case csCMYK: + CMYKtoHSV(mC, mM, mY, mK, &mH, &mS, &mV); + break; + default: + break; + } + mHSVvalid = true; +} + +void KoColor::calcCMYK() const +{ + switch(mNative) + { + case csRGB: + RGBtoCMYK(mR, mG, mB, &mC, &mM, &mY, &mK); + break; + case csLab: + LABtoCMYK(mL, ma, mB, &mC, &mM, &mY, &mK); + break; + case csHSV: + HSVtoCMYK(mH, mS, mV, &mC, &mM, &mY, &mK); + break; + default: + break; + } + mCMYKvalid = true; +} + +void KoColor::calcLAB() const +{ + switch(mNative) + { + case csRGB: + RGBtoLAB(mR, mG, mB, &mL, &ma, &mB); + break; + case csHSV: + HSVtoLAB(mH, mS, mV, &mL, &ma, &mB); + break; + case csCMYK: + CMYKtoLAB(mC, mM, mY, mK, &mL, &ma, &mB); + break; + default: + break; + } + mLABvalid = true; +} + +void KoColor::rgbChanged() const +{ + mRGBvalid = true; + mHSVvalid = false; + mCMYKvalid = false; + mLABvalid = false; +} + +void KoColor::hsvChanged() const +{ + mRGBvalid = false; + mHSVvalid = true; + mCMYKvalid = false; + mLABvalid = false; +} + +void KoColor::cmykChanged() const +{ + mRGBvalid = false; + mHSVvalid = false; + mCMYKvalid = true; + mLABvalid = false; +} + +void KoColor::labChanged() const +{ + mRGBvalid = false; + mHSVvalid = false; + mCMYKvalid = false; + mLABvalid = true; +} |