Warning, file /office/calligra/libs/pigment/KoColorConversions.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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