summaryrefslogtreecommitdiffstats
path: root/lib/kopainter/koColor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kopainter/koColor.cpp')
-rw-r--r--lib/kopainter/koColor.cpp742
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;
+}