koColor.cpp
1/* This file is part of the KDE project
2 Copyright (c) 1999 Matthias Elter (me@kde.org)
3 Copyright (c) 2001-2002 Igor Jansen (rm@kde.org)
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19*/
20
21#include "koColor.h"
22#include "kdebug.h"
23#include <cmath>
24
25KoColor::KoColor()
26{
27 // initialise to black
28 mNative = csRGB;
29 // RGB
30 mR = 0;
31 mG = 0;
32 mB = 0;
33 // HSV
34 mH = mV = 0;
35 mS = 100;
36 // CMYK
37 mC = 0;
38 mY = 0;
39 mM = 0;
40 mK = 0;
41 // Lab
42 mL = 0;
43 ma = 0;
44 mB = 0;
45 rgbChanged();
46}
47
48KoColor::KoColor(int a, int b, int c, cSpace m)
49{
50 switch(m)
51 {
52 case csRGB:
53 mR = a;
54 mG = b;
55 mB = c;
56 mNative = csRGB;
57 rgbChanged();
58 break;
59 case csHSV:
60 mH = a;
61 mS = b;
62 mV = c;
63 mNative = csHSV;
64 hsvChanged();
65 break;
66 case csLab:
67 mL = a;
68 ma = b;
69 mB = c;
70 mNative = csLab;
71 labChanged();
72 break;
73 default:
74 mR = 0;
75 mG = 0;
76 mB = 0;
77 mNative = csRGB;
78 rgbChanged();
79 }
80}
81
82KoColor::KoColor(int c, int m, int y, int k)
83{
84 mC = c;
85 mM = m;
86 mY = y;
87 mK = k;
88 mNative = csCMYK;
89 cmykChanged();
90}
91
92KoColor::KoColor(const TQColor &c)
93{
94 mR = c.red();
95 mG = c.green();
96 mB = c.blue();
97 mNative = csRGB;
98 rgbChanged();
99}
100
101KoColor::KoColor(const TQString &name)
102{
103 setNamedColor(name);
104}
105
106int KoColor::R() const
107{
108 if(!mRGBvalid)
109 calcRGB();
110 return mR;
111}
112
113int KoColor::G() const
114{
115 if(!mRGBvalid)
116 calcRGB();
117 return mG;
118}
119
120int KoColor::B() const
121{
122 if(!mRGBvalid)
123 calcRGB();
124 return mB;
125}
126
127int KoColor::H() const
128{
129 if(!mHSVvalid)
130 calcHSV();
131 return mH;
132}
133
134int KoColor::S() const
135{
136 if(!mHSVvalid)
137 calcHSV();
138 return mS;
139}
140
141int KoColor::V() const
142{
143 if(!mHSVvalid)
144 calcHSV();
145 return mV;
146}
147
148int KoColor::C() const
149{
150 if(!mCMYKvalid)
151 calcCMYK();
152 return mC;
153}
154
155int KoColor::M() const
156{
157 if(!mCMYKvalid)
158 calcCMYK();
159 return mM;
160}
161
162int KoColor::Y() const
163{
164 if(!mCMYKvalid)
165 calcCMYK();
166 return mY;
167}
168
169int KoColor::K() const
170{
171 if(!mCMYKvalid)
172 calcCMYK();
173 return mK;
174}
175
176int KoColor::L() const
177{
178 if(!mLABvalid)
179 calcLAB();
180 return mL;
181}
182
183int KoColor::a() const
184{
185 if(!mLABvalid)
186 calcLAB();
187 return ma;
188}
189
190int KoColor::b() const
191{
192 if(!mLABvalid)
193 calcLAB();
194 return mB;
195}
196
197void KoColor::rgb(int *R, int *G, int *B) const
198{
199 if(!mRGBvalid)
200 calcRGB();
201 *R = mR;
202 *G = mG;
203 *B = mB;
204}
205
206void KoColor::hsv(int *H, int *S, int *V) const
207{
208 if(!mHSVvalid)
209 calcHSV();
210 *H = mH;
211 *S = mS;
212 *V = mV;
213}
214
215void KoColor::lab(int *L, int *a, int *b) const
216{
217 if(!mLABvalid)
218 calcLAB();
219 *L = mL;
220 *a = ma;
221 *b = mB;
222}
223
224void KoColor::cmyk(int *C, int *M, int *Y, int *K) const
225{
226 if(!mCMYKvalid)
227 calcCMYK();
228 *C = mC;
229 *M = mM;
230 *Y = mY;
231 *K = mK;
232}
233
234TQString KoColor::name() const
235{
236 TQString s;
237 switch(mNative)
238 {
239 case csRGB:
240 s.sprintf("#%02x%02x%02x", R(), G(), B());
241 break;
242 case csHSV:
243 s.sprintf("$%02x%02x%02x", H(), S(), V());
244 break;
245 case csCMYK:
246 s.sprintf("@%02x%02x%02x%02x", C(), M(), Y(), K());
247 break;
248 case csLab:
249 s.sprintf("*%02x%02x%02x", L(), a(), b());
250 break;
251 default:
252 s.sprintf("#%02x%02x%02x", R(), G(), B());
253 }
254 return s;
255}
256
257TQColor KoColor::color() const
258{
259 if(!mRGBvalid)
260 calcRGB();
261 return TQColor(mR, mG, mB);
262}
263
264void KoColor::setRGB(int R, int G, int B)
265{
266 mR = R;
267 mG = G;
268 mB = B;
269 mNative = csRGB;
270 rgbChanged();
271}
272
273void KoColor::setHSV(int H, int S, int V)
274{
275 mH = H;
276 mS = S;
277 mV = V;
278 mNative = csHSV;
279 hsvChanged();
280}
281
282void KoColor::setLab(int L, int a, int b)
283{
284 mL = L;
285 ma = a;
286 mB = b;
287 mNative = csLab;
288 labChanged();
289}
290
291void KoColor::setCMYK(int C, int M, int Y, int K)
292{
293 mC = C;
294 mM = M;
295 mY = Y;
296 mK = K;
297 mNative = csCMYK;
298 cmykChanged();
299}
300
301void KoColor::setNamedColor(const TQString &name)
302{
303 switch(name[0])
304 {
305 case '#':
306 mR = (hex2int(name[1]) << 4) + hex2int(name[2]);
307 mG = (hex2int(name[3]) << 4) + hex2int(name[4]);
308 mB = (hex2int(name[5]) << 4) + hex2int(name[6]);
309 mNative = csRGB;
310 rgbChanged();
311 break;
312 case '$':
313 mH = (hex2int(name[1]) << 4) + hex2int(name[2]);
314 mS = (hex2int(name[3]) << 4) + hex2int(name[4]);
315 mV = (hex2int(name[5]) << 4) + hex2int(name[6]);
316 mNative = csHSV;
317 hsvChanged();
318 break;
319 case '@':
320 mC = (hex2int(name[1]) << 4) + hex2int(name[2]);
321 mM = (hex2int(name[3]) << 4) + hex2int(name[4]);
322 mY = (hex2int(name[5]) << 4) + hex2int(name[6]);
323 mK = (hex2int(name[7]) << 4) + hex2int(name[8]);
324 mNative = csCMYK;
325 cmykChanged();
326 break;
327 case '*':
328 mL = (hex2int(name[1]) << 4) + hex2int(name[2]);
329 ma = (hex2int(name[3]) << 4) + hex2int(name[4]);
330 mb = (hex2int(name[5]) << 4) + hex2int(name[6]);
331 mNative = csLab;
332 labChanged();
333 break;
334 default:
335 mR = 0;
336 mG = 0;
337 mB = 0;
338 mNative = csRGB;
339 rgbChanged();
340 }
341}
342
343void KoColor::setColor(const TQColor &c)
344{
345 mR = c.red();
346 mG = c.green();
347 mB = c.blue();
348 mNative = csRGB;
349 rgbChanged();
350}
351
352void KoColor::RGBtoHSV(int R, int G, int B, int *H, int *S, int *V)
353{
354 unsigned int max = R;
355 unsigned int min = R;
356 unsigned char maxValue = 0; // r = 0, g = 1, b = 2
357
358 // find maximum and minimum RGB values
359 if(static_cast<unsigned int>(G) > max)
360 {
361 max = G;
362 maxValue = 1;
363 }
364 if(static_cast<unsigned int>(B) > max)
365 {
366 max = B;
367 maxValue = 2;
368 }
369
370 if(static_cast<unsigned int>(G) < min)
371 min = G;
372 if(static_cast<unsigned int>(B) < min )
373 min = B;
374
375 int delta = max - min;
376 *V = max; // value
377 *S = max ? (510 * delta + max) / ( 2 * max) : 0; // saturation
378
379 // calc hue
380 if(*S == 0)
381 *H = -1; // undefined hue
382 else
383 {
384 switch(maxValue)
385 {
386 case 0: // red
387 if(G >= B)
388 *H = (120 * (G - B) + delta) / (2 * delta);
389 else
390 *H = (120 * (G - B + delta) + delta) / (2 * delta) + 300;
391 break;
392 case 1: // green
393 if(B > R)
394 *H = 120 + (120 * (B - R) + delta) / (2 * delta);
395 else
396 *H = 60 + (120 * (B - R + delta) + delta) / (2 * delta);
397 break;
398 case 2: // blue
399 if(R > G)
400 *H = 240 + (120 * (R - G) + delta) / (2 * delta);
401 else
402 *H = 180 + (120 * (R - G + delta) + delta) / (2 * delta);
403 break;
404 }
405 }
406}
407
408void KoColor::RGBtoLAB(int R, int G, int B, int *L, int *a, int *b)
409{
410 // Convert between RGB and CIE-Lab color spaces
411 // Uses ITU-R recommendation BT.709 with D65 as reference white.
412 // algorithm contributed by "Mark A. Ruzon" <ruzon@CS.Stanford.EDU>
413
414 double X, Y, Z, fX, fY, fZ;
415
416 X = 0.412453 * R + 0.357580 * G + 0.180423 * B;
417 Y = 0.212671 * R + 0.715160 * G + 0.072169 * B;
418 Z = 0.019334 * R + 0.119193 * G + 0.950227 * B;
419
420 X /= (255 * 0.950456);
421 Y /= 255;
422 Z /= (255 * 1.088754);
423
424 if(Y > 0.008856)
425 {
426 fY = pow(Y, 1.0 / 3.0);
427 *L = static_cast<int>(116.0 * fY - 16.0 + 0.5);
428 }
429 else
430 {
431 fY = 7.787 * Y + 16.0 / 116.0;
432 *L = static_cast<int>(903.3 * Y + 0.5);
433 }
434
435 if(X > 0.008856)
436 fX = pow(X, 1.0 / 3.0);
437 else
438 fX = 7.787 * X + 16.0 / 116.0;
439
440 if(Z > 0.008856)
441 fZ = pow(Z, 1.0 / 3.0);
442 else
443 fZ = 7.787 * Z + 16.0 / 116.0;
444
445 *a = static_cast<int>(500.0 * (fX - fY) + 0.5);
446 *b = static_cast<int>(200.0 * (fY - fZ) + 0.5);
447}
448
449void KoColor::RGBtoCMYK(int R, int G, int B, int *C, int *M, int *Y, int *K)
450{
451 // XXX: these algorithms aren't the best. See www.littlecms.com
452 // for a suitable library, or the posting by Leo Rosenthol for
453 // a better, but slower algorithm at
454 // http://lists.kde.org/?l=koffice-devel&m=106698241227054&w=2
455
456 *C = 255 - R;
457 *M = 255 - G;
458 *Y = 255 - B;
459
460 int min = (*C < *M) ? *C : *M;
461 *K = (min < *Y) ? min : *Y;
462
463 *C -= *K;
464 *M -= *K;
465 *Y -= *K;
466
467}
468
469
470void KoColor::HSVtoRGB(int H, int S, int V, int *R, int *G, int *B)
471{
472 *R = *G = *B = V;
473
474 if(S != 0 && H != -1) // chromatic
475 {
476 if(H >= 360) // angle > 360
477 H %= 360;
478
479 unsigned int f = H % 60;
480 H /= 60;
481 unsigned int p = static_cast<unsigned int>(2*V*(255-S)+255)/510;
482 unsigned int q, t;
483
484 if(H & 1)
485 {
486 q = static_cast<unsigned int>(2 * V * (15300 - S * f) + 15300) / 30600;
487 switch(H)
488 {
489 case 1:
490 *R = static_cast<int>(q);
491 *G = static_cast<int>(V);
492 *B = static_cast<int>(p);
493 break;
494 case 3:
495 *R = static_cast<int>(p);
496 *G = static_cast<int>(q);
497 *B = static_cast<int>(V);
498 break;
499 case 5:
500 *R = static_cast<int>(V);
501 *G = static_cast<int>(p);
502 *B = static_cast<int>(q);
503 break;
504 }
505 }
506 else
507 {
508 t = static_cast<unsigned int>(2 * V * (15300 - (S * (60 - f))) + 15300) / 30600;
509 switch(H)
510 {
511 case 0:
512 *R = static_cast<int>(V);
513 *G = static_cast<int>(t);
514 *B = static_cast<int>(p);
515 break;
516 case 2:
517 *R = static_cast<int>(p);
518 *G = static_cast<int>(V);
519 *B = static_cast<int>(t);
520 break;
521 case 4:
522 *R = static_cast<int>(t);
523 *G = static_cast<int>(p);
524 *B = static_cast<int>(V);
525 break;
526 }
527 }
528 }
529}
530
531void KoColor::HSVtoLAB(int H, int S, int V, int *L, int *a, int *b)
532{
533 int R, G, B;
534 HSVtoRGB(H, S, V, &R, &G, &B);
535 RGBtoLAB(R, G, B, L, a, b);
536}
537
538void KoColor::HSVtoCMYK(int H, int S, int V, int *C, int *M, int *Y, int*K)
539{
540 int R, G, B;
541 HSVtoRGB(H, S, V, &R, &G, &B);
542 RGBtoCMYK(R, G, B, C, M, Y, K);
543}
544
545void KoColor::LABtoRGB(int L, int a, int b, int *R, int *G, int *B)
546{
547 // Convert between RGB and CIE-Lab color spaces
548 // Uses ITU-R recommendation BT.709 with D65 as reference white.
549 // algorithm contributed by "Mark A. Ruzon" <ruzon@CS.Stanford.EDU>
550
551 double X, Y, Z, fX, fY, fZ;
552 int RR, GG, BB;
553
554 fY = pow((L + 16.0) / 116.0, 3.0);
555 if(fY < 0.008856)
556 fY = L / 903.3;
557 Y = fY;
558
559 if(fY > 0.008856)
560 fY = pow(fY, 1.0 / 3.0);
561 else
562 fY = 7.787 * fY + 16.0 / 116.0;
563
564 fX = a / 500.0 + fY;
565 if(fX > 0.206893)
566 X = pow(fX, 3.0);
567 else
568 X = (fX - 16.0 / 116.0) / 7.787;
569
570 fZ = fY - b / 200.0;
571 if(fZ > 0.206893)
572 Z = pow(fZ, 3.0);
573 else
574 Z = (fZ - 16.0/116.0) / 7.787;
575
576 X *= 0.950456 * 255;
577 Y *= 255;
578 Z *= 1.088754 * 255;
579
580 RR = static_cast<int>(3.240479 * X - 1.537150 * Y - 0.498535 * Z + 0.5);
581 GG = static_cast<int>(-0.969256 * X + 1.875992 * Y + 0.041556 * Z + 0.5);
582 BB = static_cast<int>(0.055648 * X - 0.204043 * Y + 1.057311 * Z + 0.5);
583
584 *R = RR < 0 ? 0 : RR > 255 ? 255 : RR;
585 *G = GG < 0 ? 0 : GG > 255 ? 255 : GG;
586 *B = BB < 0 ? 0 : BB > 255 ? 255 : BB;
587}
588
589void KoColor::LABtoHSV(int L, int a, int b, int *H, int *S, int *V)
590{
591 int R, G, B;
592 LABtoRGB(L, a, b, &R, &G, &B);
593 RGBtoHSV(R, G, B, H, S, V);
594}
595
596void KoColor::LABtoCMYK(int L, int a, int b, int *C, int *M, int *Y, int*K)
597{
598 int R, G, B;
599 LABtoRGB(L, a, b, &R, &G, &B);
600 RGBtoCMYK(R, G, B, C, M, Y, K);
601}
602
603void KoColor::CMYKtoRGB(int C, int M, int Y, int K, int *R, int *G, int *B)
604{
605 *R = 255 - (C + K);
606 *G = 255 - (M + K);
607 *B = 255 - (Y + K);
608}
609
610void KoColor::CMYKtoHSV(int C, int M, int Y, int K, int *H, int *S, int *V)
611{
612 int R, G, B;
613 CMYKtoRGB(C, M, Y, K, &R, &G, &B);
614 RGBtoHSV(R, G, B, H, S, V);
615}
616
617void KoColor::CMYKtoLAB(int C, int M, int Y, int K, int *L, int *a, int *b)
618{
619 int R, G, B;
620 CMYKtoRGB(C, M, Y, K, &R, &G, &B);
621 RGBtoLAB(R, G, B, L, a, b);
622}
623
624int KoColor::hex2int(TQChar c)
625{
626 if(c.isDigit())
627 return c.digitValue();
628 else if('A' <= (int)c && (int)c <= 'F')
629 return c - 'A' + 10;
630 else if('a' <= (int)c && (int)c <= 'f')
631 return c - 'a' + 10;
632 else
633 return 0;
634}
635
636void KoColor::calcRGB() const
637{
638 switch(mNative)
639 {
640 case csHSV:
641 HSVtoRGB(mH, mS, mV, &mR, &mG, &mB);
642 break;
643 case csLab:
644 LABtoRGB(mL, ma, mB, &mR, &mG, &mB);
645 break;
646 case csCMYK:
647 CMYKtoRGB(mC, mM, mY, mK, &mR, &mG, &mB);
648 break;
649 default:
650 break;
651 }
652 mRGBvalid = true;
653}
654
655void KoColor::calcHSV() const
656{
657 switch(mNative)
658 {
659 case csRGB:
660 RGBtoHSV(mR, mG, mB, &mH, &mS, &mV);
661 break;
662 case csLab:
663 LABtoHSV(mL, ma, mB, &mH, &mS, &mV);
664 break;
665 case csCMYK:
666 CMYKtoHSV(mC, mM, mY, mK, &mH, &mS, &mV);
667 break;
668 default:
669 break;
670 }
671 mHSVvalid = true;
672}
673
674void KoColor::calcCMYK() const
675{
676 switch(mNative)
677 {
678 case csRGB:
679 RGBtoCMYK(mR, mG, mB, &mC, &mM, &mY, &mK);
680 break;
681 case csLab:
682 LABtoCMYK(mL, ma, mB, &mC, &mM, &mY, &mK);
683 break;
684 case csHSV:
685 HSVtoCMYK(mH, mS, mV, &mC, &mM, &mY, &mK);
686 break;
687 default:
688 break;
689 }
690 mCMYKvalid = true;
691}
692
693void KoColor::calcLAB() const
694{
695 switch(mNative)
696 {
697 case csRGB:
698 RGBtoLAB(mR, mG, mB, &mL, &ma, &mB);
699 break;
700 case csHSV:
701 HSVtoLAB(mH, mS, mV, &mL, &ma, &mB);
702 break;
703 case csCMYK:
704 CMYKtoLAB(mC, mM, mY, mK, &mL, &ma, &mB);
705 break;
706 default:
707 break;
708 }
709 mLABvalid = true;
710}
711
712void KoColor::rgbChanged() const
713{
714 mRGBvalid = true;
715 mHSVvalid = false;
716 mCMYKvalid = false;
717 mLABvalid = false;
718}
719
720void KoColor::hsvChanged() const
721{
722 mRGBvalid = false;
723 mHSVvalid = true;
724 mCMYKvalid = false;
725 mLABvalid = false;
726}
727
728void KoColor::cmykChanged() const
729{
730 mRGBvalid = false;
731 mHSVvalid = false;
732 mCMYKvalid = true;
733 mLABvalid = false;
734}
735
736void KoColor::labChanged() const
737{
738 mRGBvalid = false;
739 mHSVvalid = false;
740 mCMYKvalid = false;
741 mLABvalid = true;
742}