File indexing completed on 2024-05-12 15:59:31

0001 /*
0002  *  SPDX-FileCopyrightText: 2005 Boudewijn Rempt <boud@valdyas.org>
0003  *  SPDX-FileCopyrightText: 2014 Wolthera van Hövell <griffinvalley@gmail.com>
0004  *
0005  *  SPDX-License-Identifier: LGPL-2.1-or-later
0006  */
0007 
0008 #include "KoColorConversions.h"
0009 
0010 #include <cmath>
0011 
0012 #include <QtGlobal>
0013 
0014 /**
0015  * A number of often-used conversions between color models
0016  */
0017 
0018 void rgb_to_hsv(int R, int G, int B, int *H, int *S, int *V)
0019 {
0020     unsigned int max = R;
0021     unsigned int min = R;
0022     unsigned char maxValue = 0; // r = 0, g = 1, b = 2
0023 
0024     // find maximum and minimum RGB values
0025     if (static_cast<unsigned int>(G) > max) {
0026         max = G;
0027         maxValue = 1;
0028     }
0029 
0030     if (static_cast<unsigned int>(B) > max) {
0031         max = B;
0032         maxValue = 2;
0033     }
0034 
0035     if (static_cast<unsigned int>(G) < min)
0036         min = G;
0037 
0038     if (static_cast<unsigned int>(B) < min)
0039         min = B;
0040 
0041     int delta = max - min;
0042     *V = max; // value
0043     *S = max ? (510 * delta + max) / (2 * max) : 0;  // saturation
0044 
0045     // calc hue
0046     if (*S == 0)
0047         *H = -1; // undefined hue
0048     else {
0049         switch (maxValue) {
0050         case 0:  // red
0051             if (G >= B)
0052                 *H = (120 * (G - B) + delta) / (2 * delta);
0053             else
0054                 *H = (120 * (G - B + delta) + delta) / (2 * delta) + 300;
0055             break;
0056         case 1:  // green
0057             if (B > R)
0058                 *H = 120 + (120 * (B - R) + delta) / (2 * delta);
0059             else
0060                 *H = 60 + (120 * (B - R + delta) + delta) / (2 * delta);
0061             break;
0062         case 2:  // blue
0063             if (R > G)
0064                 *H = 240 + (120 * (R - G) + delta) / (2 * delta);
0065             else
0066                 *H = 180 + (120 * (R - G + delta) + delta) / (2 * delta);
0067             break;
0068         }
0069     }
0070 }
0071 
0072 void hsv_to_rgb(int H, int S, int V, int *R, int *G, int *B)
0073 {
0074     *R = *G = *B = V;
0075 
0076     if (S != 0 && H != -1) { // chromatic
0077 
0078         if (H >= 360) {
0079             // angle > 360
0080             H %= 360;
0081         }
0082 
0083         unsigned int f = H % 60;
0084         H /= 60;
0085         unsigned int p = static_cast<unsigned int>(2 * V * (255 - S) + 255) / 510;
0086 
0087         if (H & 1) {
0088             unsigned int q = static_cast<unsigned int>(2 * V * (15300 - S * f) + 15300) / 30600;
0089             switch (H) {
0090             case 1:
0091                 *R = static_cast<int>(q);
0092                 *G = static_cast<int>(V);
0093                 *B = static_cast<int>(p);
0094                 break;
0095             case 3:
0096                 *R = static_cast<int>(p);
0097                 *G = static_cast<int>(q);
0098                 *B = static_cast<int>(V);
0099                 break;
0100             case 5:
0101                 *R = static_cast<int>(V);
0102                 *G = static_cast<int>(p);
0103                 *B = static_cast<int>(q);
0104                 break;
0105             }
0106         } else {
0107             unsigned int t = static_cast<unsigned int>(2 * V * (15300 - (S * (60 - f))) + 15300) / 30600;
0108             switch (H) {
0109             case 0:
0110                 *R = static_cast<int>(V);
0111                 *G = static_cast<int>(t);
0112                 *B = static_cast<int>(p);
0113                 break;
0114             case 2:
0115                 *R = static_cast<int>(p);
0116                 *G = static_cast<int>(V);
0117                 *B = static_cast<int>(t);
0118                 break;
0119             case 4:
0120                 *R = static_cast<int>(t);
0121                 *G = static_cast<int>(p);
0122                 *B = static_cast<int>(V);
0123                 break;
0124             }
0125         }
0126     }
0127 }
0128 
0129 #define EPSILON 1e-6
0130 #define UNDEFINED_HUE -1
0131 
0132 void RGBToHSV(float r, float g, float b, float *h, float *s, float *v)
0133 {
0134     float max = qMax(r, qMax(g, b));
0135     float min = qMin(r, qMin(g, b));
0136 
0137     *v = max;
0138 
0139     if (max > EPSILON) {
0140         *s = (max - min) / max;
0141     } else {
0142         *s = 0;
0143     }
0144 
0145     if (*s < EPSILON) {
0146         *h = UNDEFINED_HUE;
0147     } else {
0148         float delta = max - min;
0149 
0150         if (r == max) {
0151             *h = (g - b) / delta;
0152         } else if (g == max) {
0153             *h = 2 + (b - r) / delta;
0154         } else {
0155             *h = 4 + (r - g) / delta;
0156         }
0157 
0158         *h *= 60;
0159         if (*h < 0) {
0160             *h += 360;
0161         }
0162     }
0163 }
0164 
0165 void HSVToRGB(float h, float s, float v, float *r, float *g, float *b)
0166 {
0167     if (s < EPSILON || h == UNDEFINED_HUE) {
0168         // Achromatic case
0169 
0170         *r = v;
0171         *g = v;
0172         *b = v;
0173     } else {
0174         float f, p, q, t;
0175         int i;
0176 
0177         if (h > 360 - EPSILON) {
0178             h -= 360;
0179         }
0180 
0181         h /= 60;
0182         i = static_cast<int>(floor(h));
0183         f = h - i;
0184         p = v * (1 - s);
0185         q = v * (1 - (s * f));
0186         t = v * (1 - (s * (1 - f)));
0187 
0188         switch (i) {
0189         case 0:
0190             *r = v;
0191             *g = t;
0192             *b = p;
0193             break;
0194         case 1:
0195             *r = q;
0196             *g = v;
0197             *b = p;
0198             break;
0199         case 2:
0200             *r = p;
0201             *g = v;
0202             *b = t;
0203             break;
0204         case 3:
0205             *r = p;
0206             *g = q;
0207             *b = v;
0208             break;
0209         case 4:
0210             *r = t;
0211             *g = p;
0212             *b = v;
0213             break;
0214         case 5:
0215             *r = v;
0216             *g = p;
0217             *b = q;
0218             break;
0219         }
0220     }
0221 }
0222 
0223 void rgb_to_hls(quint8 red, quint8 green, quint8 blue, float * hue, float * lightness, float * saturation)
0224 {
0225     float r = red / 255.0;
0226     float g = green / 255.0;
0227     float b = blue / 255.0;
0228     float h = 0;
0229     float l = 0;
0230     float s = 0;
0231 
0232     float max, min, delta;
0233 
0234     max = qMax(r, g);
0235     max = qMax(max, b);
0236 
0237     min = qMin(r, g);
0238     min = qMin(min, b);
0239 
0240     delta = max - min;
0241 
0242     l = (max + min) / 2;
0243 
0244     if (delta == 0) {
0245         // This is a gray, no chroma...
0246         h = 0;
0247         s = 0;
0248     } else {
0249         if (l < 0.5)
0250             s = delta / (max + min);
0251         else
0252             s = delta / (2 - max - min);
0253 
0254         float delta_r, delta_g, delta_b;
0255 
0256         delta_r = ((max - r) / 6) / delta;
0257         delta_g = ((max - g) / 6) / delta;
0258         delta_b = ((max - b) / 6) / delta;
0259 
0260         if (r == max)
0261             h = delta_b - delta_g;
0262         else if (g == max)
0263             h = (1.0 / 3) + delta_r - delta_b;
0264         else if (b == max)
0265             h = (2.0 / 3) + delta_g - delta_r;
0266 
0267         if (h < 0) h += 1;
0268         if (h > 1) h += 1;
0269 
0270     }
0271 
0272     *hue = h * 360;
0273     *saturation = s;
0274     *lightness = l;
0275 }
0276 
0277 float hue_value(float n1, float n2, float hue)
0278 {
0279     if (hue > 360)
0280         hue = hue - 360;
0281     else if (hue < 0)
0282         hue = hue + 360;
0283     if (hue < 60)
0284         return n1 + (((n2 - n1) * hue) / 60);
0285     else if (hue < 180)
0286         return n2;
0287     else if (hue < 240)
0288         return n1 + (((n2 - n1) *(240 - hue)) / 60);
0289     else return n1;
0290 }
0291 
0292 
0293 void hls_to_rgb(float h, float l, float s, quint8 * r, quint8 * g, quint8 * b)
0294 {
0295     float m1, m2;
0296 
0297     if (l <= 0.5)
0298         m2 = l * (1 + s);
0299     else
0300         m2 = l + s - l * s;
0301 
0302     m1 = 2 * l - m2;
0303 
0304     *r = (quint8)(hue_value(m1, m2, h + 120) * 255 + 0.5);
0305     *g = (quint8)(hue_value(m1, m2, h) * 255 + 0.5);
0306     *b = (quint8)(hue_value(m1, m2, h - 120) * 255 + 0.5);
0307 
0308 }
0309 
0310 void rgb_to_hls(quint8 r, quint8 g, quint8 b, int * h, int * l, int * s)
0311 {
0312     float hue, saturation, lightness;
0313 
0314     rgb_to_hls(r, g, b, &hue, &lightness, &saturation);
0315     *h = (int)(hue + 0.5);
0316     *l = (int)(lightness * 255 + 0.5);
0317     *s = (int)(saturation * 255 + 0.5);
0318 }
0319 
0320 void hls_to_rgb(int h, int l, int s, quint8 * r, quint8 * g, quint8 * b)
0321 {
0322     float hue = h;
0323     float lightness = l / 255.0;
0324     float saturation = s / 255.0;
0325 
0326     hls_to_rgb(hue, lightness, saturation, r, g, b);
0327 }
0328 
0329 /*
0330 A Fast HSL-to-RGB Transform
0331 by Ken Fishkin
0332 from "Graphics Gems", Academic Press, 1990
0333 */
0334 
0335 void RGBToHSL(float r, float g, float b, float *h, float *s, float *l)
0336 {
0337     float v;
0338     float m;
0339     float vm;
0340     float r2, g2, b2;
0341 
0342     v = qMax(r, g);
0343     v = qMax(v, b);
0344     m = qMin(r, g);
0345     m = qMin(m, b);
0346 
0347     if ((*l = (m + v) / 2.0) <= 0.0) {
0348         *h = UNDEFINED_HUE;
0349         *s = 0;
0350         return;
0351     }
0352     if ((*s = vm = v - m) > 0.0) {
0353         *s /= (*l <= 0.5) ? (v + m) :
0354               (2.0 - v - m) ;
0355     } else {
0356         *h = UNDEFINED_HUE;
0357         return;
0358     }
0359 
0360 
0361     r2 = (v - r) / vm;
0362     g2 = (v - g) / vm;
0363     b2 = (v - b) / vm;
0364 
0365     if (r == v)
0366         *h = (g == m ? 5.0 + b2 : 1.0 - g2);
0367     else if (g == v)
0368         *h = (b == m ? 1.0 + r2 : 3.0 - b2);
0369     else
0370         *h = (r == m ? 3.0 + g2 : 5.0 - r2);
0371 
0372     *h *= 60;
0373     *h = fmod(*h, 360.0);
0374 }
0375 
0376 void HSLToRGB(float h, float sl, float l, float *r, float *g, float *b)
0377 
0378 {
0379     float v;
0380 
0381     v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl);
0382     if (v <= 0) {
0383         *r = *g = *b = 0.0;
0384     } else {
0385         float m;
0386         float sv;
0387         int sextant;
0388         float fract, vsf, mid1, mid2;
0389 
0390         m = l + l - v;
0391         sv = (v - m) / v;
0392         h = fmod(h, 360.0);
0393         h /= 60.0;
0394         sextant = static_cast<int>(h);
0395         fract = h - sextant;
0396         vsf = v * sv * fract;
0397         mid1 = m + vsf;
0398         mid2 = v - vsf;
0399         switch (sextant) {
0400         case 0: *r = v; *g = mid1; *b = m; break;
0401         case 1: *r = mid2; *g = v; *b = m; break;
0402         case 2: *r = m; *g = v; *b = mid1; break;
0403         case 3: *r = m; *g = mid2; *b = v; break;
0404         case 4: *r = mid1; *g = m; *b = v; break;
0405         case 5: *r = v; *g = m; *b = mid2; break;
0406         }
0407     }
0408 }
0409 
0410 //functions for converting from and back to HSI
0411 void HSIToRGB(const qreal h,const qreal s, const qreal i, qreal *red, qreal *green, qreal *blue)
0412 {//This function takes H, S and I values, which are converted to rgb.
0413     qreal onethird = 1.0/3.0;
0414     HSYToRGB(h, s, i, red, green, blue, onethird, onethird, onethird);
0415 }
0416 void RGBToHSI(qreal r,qreal g, qreal b, qreal *h, qreal *s, qreal *i)
0417 {
0418     qreal onethird = 1.0/3.0;
0419     RGBToHSY(r, g, b, h, s, i, onethird, onethird, onethird);
0420 
0421 }
0422 //functions for converting from and back to hsy'
0423 void HSYToRGB(const qreal h,const qreal s, const qreal y, qreal *red, qreal *green, qreal *blue, qreal R, qreal G, qreal B)
0424 {//This function takes H, S and Y values, which are converted to rgb.
0425 //Those are then used to create a qcolor.
0426     qreal hue = 0.0;
0427     qreal sat = 0.0;
0428     qreal luma = 0.0;
0429     if (h>1.0 || h<0.0){hue=fmod(h, 1.0);} else {hue=h;}
0430     if (s<0.0){sat=0.0;} else {sat=s;}
0431     //if (y>1.0){luma=1.0;}
0432     if (y<0.0){luma=0.0;}
0433     else {luma=y;}
0434     
0435     qreal segment = 0.166667;//1/6;
0436     qreal r=0.0;
0437     qreal g=0.0;
0438     qreal b=0.0;
0439 //weights for rgb to Y'(Luma), these are the same weights used in color space maths and the desaturate.
0440 //This is not luminance or luminosity, it just quacks like it.
0441     //qreal R=0.299;
0442     //qreal G=0.587;
0443     //qreal B=0.114;
0444 //The intermediary variables for the weighted HSL forumala, based on the HSL in KoColorConversions.
0445     qreal max_sat, m, fract, luma_a, chroma, x;
0446     if (hue >= 0.0 && hue < (segment) ) {
0447             //need to treat this as a weighted hsl thingy.
0448             //so first things first, at which luma is the maximum saturation for this hue?
0449             //between R and G+R (yellow)
0450         max_sat = R + ( G*(hue*6) );    
0451         if (luma<=max_sat){luma_a = (luma/max_sat)*0.5; chroma=sat*2*luma_a;}
0452         else {luma_a = ((luma-max_sat)/(1-max_sat)*0.5)+0.5; chroma=sat*(2-2*luma_a);}
0453 
0454         fract = hue*6.0;
0455         x = (1-fabs(fmod(fract,2)-1))*chroma;
0456         r = chroma; g=x; b=0;
0457         m = luma-( (R*r)+(B*b)+(G*g) );
0458         r += m; g += m; b += m;
0459     } else if (hue >= (segment) && hue < (2.0*segment) ) {
0460         max_sat = (G+R) - (R*(hue-segment)*6);
0461 
0462         if (luma<max_sat) {
0463             luma_a = (luma/max_sat)*0.5; chroma=sat*(2*luma_a);
0464         } else {
0465             luma_a = ((luma-max_sat)/(1-max_sat)*0.5)+0.5; chroma=sat*(2-2*luma_a);
0466         }
0467 
0468         fract = hue*6.0;
0469         x = (1-fabs(fmod(fract,2)-1) )*chroma;
0470         r = x; g=chroma; b=0;
0471         m = luma-( (R*r)+(B*b)+(G*g) );
0472         r += m; g += m; b += m;
0473     } else if (hue >= (2.0*segment) && hue < (3.0*segment) ) {
0474         max_sat = G + (B*(hue-2.0*segment)*6);
0475         if (luma<max_sat) { 
0476             luma_a = (luma/max_sat)*0.5; chroma=sat*(2*luma_a);
0477         } else {
0478             luma_a = ((luma-max_sat)/(1-max_sat)*0.5)+0.5; chroma=sat*(2-2*luma_a);
0479         }
0480         fract = hue*6.0;
0481         x = (1-fabs(fmod(fract,2)-1) )*chroma;
0482         r = 0; g=chroma; b=x;
0483         m = luma-( (R*r)+(B*b)+(G*g) );
0484         r += m; g += m; b += m;
0485     } else if (hue >= (3.0*segment) && hue < (4.0*segment) ) {
0486         max_sat = (G+B) - (G*(hue-3.0*segment)*6);  
0487         if (luma<max_sat){
0488             luma_a = (luma/max_sat)*0.5; chroma=sat*(2*luma_a);
0489         } else {
0490             luma_a = ((luma-max_sat)/(1-max_sat)*0.5)+0.5; chroma=sat*(2-2*luma_a);
0491         }
0492 
0493         fract = hue*6.0;
0494         x = (1-fabs(fmod(fract,2)-1) )*chroma;
0495         r = 0; g=x; b=chroma;
0496         m = luma-( (R*r)+(B*b)+(G*g) );
0497         r += m; g += m; b += m;
0498     } else if (hue >= (4.0*segment) && hue < (5*segment) ) {
0499         max_sat = B + (R*((hue-4.0*segment)*6));    
0500         if (luma<max_sat) {
0501             luma_a = (luma/max_sat)*0.5; chroma=sat*(2*luma_a);
0502         } else {
0503             luma_a = ((luma-max_sat)/(1-max_sat)*0.5)+0.5; chroma=sat*(2-2*luma_a);
0504         }
0505         fract = hue*6.0;
0506         x = (1-fabs(fmod(fract,2)-1) )*chroma;
0507         r = x; g=0; b=chroma;
0508         m = luma-( (R*r)+(B*b)+(G*g) );
0509         r += m; g += m; b += m;
0510     } else if (hue >= (5.0*segment) && hue <= 1.0) {
0511         max_sat = (B+R) - (B*(hue-5.0*segment)*6);  
0512         if (luma<max_sat){
0513             luma_a = (luma/max_sat)*0.5; chroma=sat*(2*luma_a);
0514         } else {
0515             luma_a = ((luma-max_sat)/(1-max_sat)*0.5)+0.5; chroma=sat*(2-2*luma_a);
0516         }
0517         fract = hue*6.0;
0518         x = (1-fabs(fmod(fract,2)-1) )*chroma;
0519         r = chroma; g=0; b=x;
0520         m = luma-( (R*r)+(B*b)+(G*g) );
0521         r += m; g += m; b += m;
0522     } else {
0523         r=0.0;
0524         g=0.0;
0525         b=0.0;
0526     }
0527 
0528     //dbgPigment<<"red: "<<r<<", green: "<<g<<", blue: "<<b;
0529     //if (r>1.0){r=1.0;}
0530     //if (g>1.0){g=1.0;}
0531     //if (b>1.0){b=1.0;}
0532     //don't limit upwards due to floating point.
0533     if (r<0.0){r=0.0;}
0534     if (g<0.0){g=0.0;}
0535     if (b<0.0){b=0.0;}
0536 
0537     *red=r;
0538     *green=g;
0539     *blue=b;
0540 }
0541 void RGBToHSY(const qreal r,const qreal g,const qreal b, qreal *h, qreal *s, qreal *y, qreal R, qreal G, qreal B)
0542 {
0543 //This is LUMA btw, not Luminance.
0544 //Using these RGB values, we calculate the H, S and I.
0545     qreal red; qreal green; qreal blue;
0546     if (r<0.0){red=0.0;} else {red=r;}
0547     if (g<0.0){green=0.0;} else {green=g;}
0548     if (b<0.0){blue=0.0;} else {blue=b;}
0549 
0550     qreal minval = qMin(r, qMin(g, b));
0551     qreal maxval = qMax(r, qMax(g, b));
0552     qreal hue = 0.0;
0553     qreal sat = 0.0;
0554     qreal luma = 0.0;
0555     //weights for rgb, these are the same weights used in color space maths and the desaturate.
0556     //qreal R=0.299;
0557     //qreal G=0.587;
0558     //qreal B=0.114;
0559     luma=(R*red+G*green+B*blue);
0560     qreal luma_a=luma;//defined later
0561     qreal chroma = maxval-minval;
0562     qreal max_sat=0.5;
0563     if(chroma==0) {
0564         hue = 0.0;
0565         sat = 0.0;
0566     } else {
0567         //the following finds the hue
0568 
0569         if(maxval==r) {
0570             //hue = fmod(((g-b)/chroma), 6.0);
0571             //above doesn't work so let's try this one:
0572             if (minval==b) {
0573                 hue = (g-b)/chroma;
0574             } else {
0575                 hue = (g-b)/chroma + 6.0;
0576             }
0577         } else if(maxval==g) {
0578             hue = (b-r)/chroma + 2.0;
0579         } else if(maxval==b) {
0580             hue = (r-g)/chroma + 4.0;
0581         }
0582 
0583         hue /=6.0;//this makes sure that hue is in the 0-1.0 range.
0584         //Most HSY formula will tell you that Sat=Chroma. However, this HSY' formula tries to be a
0585         //weighted HSL formula, where instead of 0.5, we search for a Max_Sat value, which is the Y'
0586         //at which the saturation is maximum.
0587         //This requires using the hue, and combining the weighting values accordingly.
0588         qreal segment = 0.166667;
0589         if (hue>1.0 || hue<0.0) {
0590             hue=fmod(hue, 1.0);
0591         }
0592 
0593         if (hue>=0.0 && hue<segment) {
0594             max_sat = R + G*(hue*6);
0595         } else if (hue>=segment && hue<(2.0*segment)) {
0596             max_sat = (G+R) - R*((hue-segment)*6);
0597         } else if (hue>=(2.0*segment) && hue<(3.0*segment)) {
0598             max_sat = G + B*((hue-2.0*segment)*6);
0599         } else if (hue>=(3.0*segment) && hue<(4.0*segment)) {
0600             max_sat = (B+G) - G*((hue-3.0*segment)*6);
0601         } else if (hue>=(4.0*segment) && hue<(5.0*segment)) {
0602             max_sat =  (B) + R*((hue-4.0*segment)*6);
0603         } else if (hue>=(5.0*segment) && hue<=1.0) {
0604             max_sat = (R+B) - B*((hue-5.0*segment)*6);
0605         } else {
0606             max_sat=0.5;
0607         }
0608 
0609         if(max_sat>1.0 || max_sat<0.0){ //This should not show up during normal use
0610             max_sat=(fmod(max_sat,1.0));
0611         } //If it does, it'll try to correct, but it's not good!
0612 
0613             //This is weighting the luma for the saturation)
0614         if (luma <= max_sat) {
0615             luma_a = (luma/max_sat)*0.5;
0616         } else{
0617             luma_a = ((luma-max_sat)/(1-max_sat)*0.5)+0.5;
0618         }
0619             
0620         if (chroma > 0.0) {
0621             sat = (luma <= max_sat) ? (chroma/ (2*luma_a) ) :(chroma/(2.0-(2*luma_a) ) ) ;
0622         }
0623     }
0624 
0625     //if (sat>1.0){sat=1.0;}
0626     //if (luma>1.0){luma=1.0;}
0627     if (sat<0.0){sat=0.0;}
0628     if (luma<0.0){luma=0.0;}
0629 
0630     *h=hue;
0631     *s=sat;
0632     *y=luma;
0633 
0634 
0635 }
0636 //Extra: Functions for converting from and back to HCI. Where the HSI function is forced cylindrical, HCI is a 
0637 //double cone. This is for compatibility purposes, and of course, making future programmers who expect a double-cone
0638 // function less sad. These algorithms were taken from wikipedia.
0639 
0640 void HCIToRGB(const qreal h, const qreal c, const qreal i, qreal *red, qreal *green, qreal *blue)
0641 {
0642 //This function may not be correct, but it's based on the HCY function on the basis of seeing HCI as similar
0643 //to the weighted HCY, but assuming that the weights are the same(one-third).
0644     qreal hue=0.0;
0645     qreal chroma=0.0;
0646     qreal intensity=0.0;    
0647     if(i<0.0){intensity = 0.0;} else{intensity = i;}
0648     if (h>1.0 || h<0.0){hue=fmod(h, 1.0);} else {hue=h;}
0649     if(c<0.0){chroma = 0.0;} else{chroma = c;}
0650     const qreal onethird = 1.0/3.0;
0651     qreal r=0.0;
0652     qreal g=0.0;
0653     qreal b=0.0;
0654     
0655     int fract = static_cast<int>(hue*6.0);
0656     qreal x = (1-fabs(fmod(hue*6.0,2)-1) )*chroma;
0657     switch (fract) {
0658         case 0:r = chroma; g=x; b=0;break;
0659         case 1:r = x; g=chroma; b=0;break;
0660         case 2:r = 0; g=chroma; b=x;break;
0661         case 3:r = 0; g=x; b=chroma;break;
0662         case 4:r = x; g=0; b=chroma;break;
0663         case 5:r = chroma; g=0; b=x;break;
0664     }
0665     qreal m = intensity-( onethird*(r+g+b) );
0666     r += m; g += m; b += m;
0667 
0668     *red=r;
0669     *green=g;
0670     *blue=b;
0671 }
0672 
0673 void RGBToHCI(const qreal r,const qreal g,const qreal b, qreal *h, qreal *c, qreal *i)
0674 {
0675     qreal minval = qMin(r, qMin(g, b));
0676     qreal maxval = qMax(r, qMax(g, b));
0677     qreal hue = 0.0;
0678     qreal sat = 0.0;
0679     qreal intensity = 0.0;
0680     intensity=(r+g+b)/3.0;
0681     qreal chroma = maxval-minval;
0682     if(chroma==0) {
0683         hue = 0.0;
0684         sat = 0.0;
0685     } else {
0686         //the following finds the hue
0687 
0688         if(maxval==r) {
0689             if (minval==b) {
0690                 hue = (g-b)/chroma;
0691             } else {
0692                 hue = (g-b)/chroma + 6.0;
0693             }
0694         } else if(maxval==g) {
0695             hue = (b-r)/chroma + 2.0;
0696         } else if(maxval==b) {
0697             hue = (r-g)/chroma + 4.0;
0698         }
0699         hue /=6.0;//this makes sure that hue is in the 0-1.0 range.
0700         sat= 1-(minval/intensity);
0701     }
0702 
0703     *h=hue;
0704     *c=sat;
0705     *i=intensity;
0706 
0707 }
0708 void HCYToRGB(const qreal h, const qreal c, const qreal y, qreal *red, qreal *green, qreal *blue, qreal R, qreal G, qreal B)
0709 {
0710     qreal hue=0.0;
0711     qreal chroma=c;
0712     qreal luma=y;
0713     if (h>1.0 || h<0.0){hue=(fmod((h*2.0), 2.0))/2.0;} else {hue=h;}
0714     //const qreal R=0.299;
0715     //const qreal G=0.587;
0716     //const qreal B=0.114;
0717     qreal r=0.0;
0718     qreal g=0.0;
0719     qreal b=0.0;
0720 
0721     int fract =static_cast<int>(hue*6.0); 
0722     qreal x = (1-fabs(fmod(hue*6.0,2)-1) )*chroma;
0723     switch (fract) {
0724         case 0:r = chroma; g=x; b=0;break;
0725         case 1:r = x; g=chroma; b=0;break;
0726         case 2:r = 0; g=chroma; b=x;break;
0727         case 3:r = 0; g=x; b=chroma;break;
0728         case 4:r = x; g=0; b=chroma;break;
0729         case 5:r = chroma; g=0; b=x;break;
0730     }
0731     qreal m = luma-( (R*r)+(B*b)+(G*g) );
0732     r += m; g += m; b += m;
0733 
0734     *red=r;
0735     *green=g;
0736     *blue=b;
0737 }
0738 
0739 void RGBToHCY(const qreal r,const qreal g,const qreal b, qreal *h, qreal *c, qreal *y, qreal R, qreal G, qreal B)
0740 {
0741     qreal minval = qMin(r, qMin(g, b));
0742     qreal maxval = qMax(r, qMax(g, b));
0743     qreal hue = 0.0;
0744     qreal chroma = 0.0;
0745     qreal luma = 0.0;
0746     //weights for rgb, these are the same weights used in color space maths and the desaturate.
0747     //qreal R=0.299;
0748     //qreal G=0.587;
0749     //qreal B=0.114;
0750     luma=(R*r+G*g+B*b);
0751     chroma = maxval-minval;
0752 
0753     if(chroma==0) {
0754         hue = 0.0;
0755     }
0756     else {
0757         //the following finds the hue
0758 
0759         if(maxval==r) {
0760             //hue = fmod(((g-b)/chroma), 6.0);
0761             //above doesn't work so let's try this one:
0762             if (minval==b) {
0763                 hue = (g-b)/chroma;
0764             } else {
0765                 hue = (g-b)/chroma + 6.0;
0766             }
0767         } else if(maxval==g) {
0768             hue = (b-r)/chroma + 2.0;
0769         } else if(maxval==b) {
0770             hue = (r-g)/chroma + 4.0;
0771         }
0772         hue /=6.0;//this makes sure that hue is in the 0-1.0 range.
0773     }
0774     if (chroma<0.0){chroma=0.0;}
0775     if (luma<0.0){luma=0.0;}
0776 
0777     *h=qBound(0.0,hue,1.0);
0778     *c=chroma;
0779     *y=luma;
0780 
0781 }
0782 void RGBToYUV(const qreal r,const qreal g,const qreal b, qreal *y, qreal *u, qreal *v, qreal R, qreal G, qreal B)
0783 {
0784     qreal uvmax = 0.5;
0785     qreal luma = R*r+G*g+B*b;
0786     qreal chromaBlue = uvmax*( (b - luma) / (1.0-B) );
0787     qreal chromaRed  = uvmax*( (r - luma) / (1.0-R) );
0788 
0789     *y = luma; //qBound(0.0,luma,1.0);
0790     *u = chromaBlue+uvmax;//qBound(0.0,chromaBlue+ uvmax,1.0);
0791     *v = chromaRed+uvmax;//qBound(0.0,chromaRed + uvmax,1.0);
0792 }
0793 void YUVToRGB(const qreal y, const qreal u, const qreal v, qreal *r, qreal *g, qreal *b, qreal R, qreal G, qreal B)
0794 {
0795     qreal uvmax = 0.5;
0796     qreal chromaBlue = u-uvmax;//qBound(0.0,u,1.0)- uvmax;//put into -0.5-+0.5 range//
0797     qreal chromaRed = v-uvmax;//qBound(0.0,v,1.0)- uvmax;
0798 
0799     qreal negB  = 1.0-B;
0800     qreal negR  = 1.0-R;
0801     qreal red   = y+(chromaRed  * (negR / uvmax) );
0802     qreal green = y-(chromaBlue * ((B*negB) / (uvmax*G)) ) - (chromaRed* ((R*negR) / (uvmax*G)));
0803     qreal blue  = y+(chromaBlue * (negB / uvmax) );
0804 
0805     *r=red;//qBound(0.0,red  ,1.0);
0806     *g=green;//qBound(0.0,green,1.0);
0807     *b=blue;//qBound(0.0,blue ,1.0);
0808 }
0809 
0810 void LabToLCH(const qreal l, const qreal a, const qreal b, qreal *L, qreal *C, qreal *H)
0811 {
0812     qreal atemp =  (a - 0.5)*10.0;//the multiplication is only so that we get out of floating-point maths
0813     qreal btemp =  (b - 0.5)*10.0;
0814     *L=qBound(0.0,l,1.0);
0815     *C=sqrt( pow(atemp,2.0) + pow(btemp,2.0) )*0.1;
0816     qreal hue = (atan2(btemp,atemp))* 180.0 / M_PI;
0817     
0818     if (hue<0.0) {
0819         hue+=360.0;
0820     } else {
0821         hue = fmod(hue, 360.0);
0822     }
0823     *H=hue/360.0;
0824 }
0825 
0826 void LCHToLab(const qreal L, const qreal C, const qreal H, qreal *l, qreal *a, qreal *b)
0827 {
0828     qreal chroma = qBound(0.0,C,1.0);
0829     qreal hue = (qBound(0.0,H,1.0)*360.0)* M_PI / 180.0;
0830     *l=qBound(0.0,L,1.0);
0831     *a=(chroma * cos(hue) ) + 0.5;
0832     *b=(chroma * sin(hue) ) + 0.5;
0833 }
0834 
0835 void XYZToxyY(const qreal X, const qreal Y, const qreal Z, qreal *x, qreal *y, qreal *yY)
0836 {
0837     qreal Xb = X;
0838     qreal Yb = Y;
0839     qreal Zb = Z;
0840     *x=Xb/(Xb+Yb+Zb);
0841     *y=Yb/(Xb+Yb+Zb);
0842     *yY=Yb;
0843 }  
0844 void xyYToXYZ(const qreal x, const qreal y, const qreal yY, qreal *X, qreal *Y, qreal *Z)
0845 {
0846     qreal xb = x;
0847     qreal yb = y;
0848     qreal yYb = yY;
0849     *X=(xb*yYb)/yb;
0850     *Z=((1.0-xb-yb)*yYb)/yb;
0851     *Y=yYb;
0852 }
0853 
0854 void CMYToCMYK(qreal *c, qreal *m, qreal *y, qreal *k)
0855 {
0856     qreal cyan, magenta, yellow, key = 1.0;
0857     cyan    = *c;
0858     magenta = *m;
0859     yellow  = *y;
0860     if ( cyan    < key ) {key = cyan;}
0861     if ( magenta < key ) {key = magenta;}
0862     if ( yellow  < key ) {key = yellow;}
0863     
0864     if ( key == 1 ) { //Black
0865         cyan    = 0;
0866         magenta = 0;
0867         yellow  = 0;
0868     }
0869     else {
0870         cyan    = ( cyan    - key ) / ( 1.0 - key );
0871         magenta = ( magenta - key ) / ( 1.0 - key );
0872         yellow  = ( yellow  - key ) / ( 1.0 - key );
0873     }
0874     
0875     *c=qBound(0.0,cyan   ,1.0);
0876     *m=qBound(0.0,magenta,1.0);
0877     *y=qBound(0.0,yellow ,1.0);
0878     *k=qBound(0.0,key    ,1.0);
0879 }
0880 
0881 /*code from easyrgb.com*/
0882 void CMYKToCMY(qreal *c, qreal *m, qreal *y, qreal *k)
0883 {
0884     qreal key     = *k;
0885     qreal cyan    = *c;
0886     qreal magenta = *m;
0887     qreal yellow  = *y;
0888     
0889     cyan    = ( cyan    * ( 1.0 - key ) + key );
0890     magenta = ( magenta * ( 1.0 - key ) + key );
0891     yellow  = ( yellow  * ( 1.0 - key ) + key );
0892     
0893     *c=qBound(0.0,cyan   ,1.0);
0894     *m=qBound(0.0,magenta,1.0);
0895     *y=qBound(0.0,yellow ,1.0);
0896 }