/* 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 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(G) > max) { max = G; maxValue = 1; } if(static_cast(B) > max) { max = B; maxValue = 2; } if(static_cast(G) < min) min = G; if(static_cast(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" 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(116.0 * fY - 16.0 + 0.5); } else { fY = 7.787 * Y + 16.0 / 116.0; *L = static_cast(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(500.0 * (fX - fY) + 0.5); *b = static_cast(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(2*V*(255-S)+255)/510; unsigned int q, t; if(H & 1) { q = static_cast(2 * V * (15300 - S * f) + 15300) / 30600; switch(H) { case 1: *R = static_cast(q); *G = static_cast(V); *B = static_cast(p); break; case 3: *R = static_cast(p); *G = static_cast(q); *B = static_cast(V); break; case 5: *R = static_cast(V); *G = static_cast(p); *B = static_cast(q); break; } } else { t = static_cast(2 * V * (15300 - (S * (60 - f))) + 15300) / 30600; switch(H) { case 0: *R = static_cast(V); *G = static_cast(t); *B = static_cast(p); break; case 2: *R = static_cast(p); *G = static_cast(V); *B = static_cast(t); break; case 4: *R = static_cast(t); *G = static_cast(p); *B = static_cast(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" 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(3.240479 * X - 1.537150 * Y - 0.498535 * Z + 0.5); GG = static_cast(-0.969256 * X + 1.875992 * Y + 0.041556 * Z + 0.5); BB = static_cast(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; }