Back to index

nux  3.0.0
Color.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright 2010-2012 Inalogic« Inc.
00003  *
00004  * This program is free software: you can redistribute it and/or modify it
00005  * under the terms of the GNU Lesser General Public License, as
00006  * published by the  Free Software Foundation; either version 2.1 or 3.0
00007  * of the License.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranties of
00011  * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
00012  * PURPOSE.  See the applicable version of the GNU Lesser General Public
00013  * License for more details.
00014  *
00015  * You should have received a copy of both the GNU Lesser General Public
00016  * License along with this program. If not, see <http://www.gnu.org/licenses/>
00017  *
00018  * Authored by: Jay Taoko <jaytaoko@inalogic.com>
00019  *
00020  */
00021 
00022 #include "Color.h"
00023 #include "ColorPrivate.h"
00024 
00025 #include <cmath>
00026 #include <cstdlib>
00027 #include <algorithm>
00028 
00029 
00030 #define NUX_RGBA_GET_ALPHA(rgba)      ((rgba) >> 24)
00031 #define NUX_RGBA_GET_RED(rgba)        (((rgba) >> 16) & 0xff)
00032 #define NUX_RGBA_GET_GREEN(rgba)      (((rgba) >> 8) & 0xff)
00033 #define NUX_RGBA_GET_BLUE(rgba)       ((rgba) & 0xff)
00034 
00035 namespace nux
00036 {
00037 namespace color
00038 {
00039   Color::Color()
00040   : red(0.0f)
00041   , green(0.0f)
00042   , blue(0.0f)
00043   , alpha(1.0f)
00044   , premultiplied_(false)
00045   {}
00046 
00047   Color::Color(RedGreenBlue const& rgb, float a)
00048   : red(rgb.red)
00049   , green(rgb.green)
00050   , blue(rgb.blue)
00051   , alpha(a)
00052   , premultiplied_(false)
00053   {}
00054 
00055   Color::Color (unsigned int c)
00056   : red(NUX_RGBA_GET_RED(c) / 255.0f)
00057   , green(NUX_RGBA_GET_GREEN(c) / 255.0f)
00058   , blue(NUX_RGBA_GET_BLUE(c) / 255.0f)
00059   , alpha(NUX_RGBA_GET_ALPHA(c) / 255.0f)
00060   , premultiplied_(false)
00061   {}
00062 
00063   Color::Color(int r, int g, int b)
00064   : red(r / 255.0f)
00065   , green(g / 255.0f)
00066   , blue(b / 255.0f)
00067   , alpha(1.0f)
00068   , premultiplied_(false)
00069   {}
00070 
00071   Color::Color(float r, float g, float b, float a)
00072   : red(r)
00073   , green(g)
00074   , blue(b)
00075   , alpha(a)
00076   , premultiplied_(false)
00077   {}
00078 
00079   Color::Color(std::string const& hex)
00080   : red(0.0f)
00081   , green(0.0f)
00082   , blue(0.0f)
00083   , alpha(1.0f)
00084   , premultiplied_(false)
00085   {
00086     HexToRGBA(hex, red, green, blue, alpha);
00087   }
00088 
00089   Color Color::GetPremultiplied()
00090   {
00091     if (premultiplied_)
00092     {
00093       // Already pre-multiplied. Return *this;
00094       return *this;
00095     }
00096 
00097     Color c;
00098     c.SetPremultiplied(red, green, blue, alpha);
00099     return c;
00100   }
00101 
00102   void Color::SetPremultiplied(float r, float g, float b, float a)
00103   {
00104     red   = r * a;
00105     green = g * a;
00106     blue  = b * a;
00107     alpha = a;
00108     premultiplied_ = true;
00109   }
00110 
00111   bool Color::IsPremultiplied()
00112   {
00113     return premultiplied_;
00114   }
00115 
00116   bool operator == (const Color& lhs, const Color& rhs)
00117   {
00118     return (lhs.red == rhs.red &&
00119             lhs.green == rhs.green &&
00120             lhs.blue == rhs.blue &&
00121             lhs.alpha == rhs.alpha &&
00122             lhs.premultiplied_ == rhs.premultiplied_);
00123   }
00124 
00125   bool operator != (const Color& lhs, const Color& rhs)
00126   {
00127     return !(lhs == rhs);
00128   }
00129 
00130   Color RandomColor()
00131   {
00132     return Color(RandomColorINT());
00133   }
00134 
00135   unsigned int RandomColorINT()
00136   {
00137     // std::rand isn't defined to be more random than 2^15, so we need
00138     // to generate the full unsigned in chunks.
00139     return (((std::rand() % 255) << 24) |
00140             ((std::rand() % 255) << 16) |
00141             ((std::rand() % 255) << 8) |
00142             (std::rand() % 255));
00143   }
00144 
00145   Color operator + (Color const& lhs, Color const& rhs)
00146   {
00147     return Color(lhs.red + rhs.red,
00148                  lhs.green + rhs.green,
00149                  lhs.blue + rhs.blue,
00150                  lhs.alpha + rhs.alpha);
00151   }
00152 
00153   Color operator - (Color const& lhs, Color const& rhs)
00154   {
00155     return Color(lhs.red - rhs.red,
00156                  lhs.green - rhs.green,
00157                  lhs.blue - rhs.blue,
00158                  lhs.alpha - rhs.alpha);
00159   }
00160 
00161   Color operator + (float scalar, Color const& c)
00162   {
00163     return Color(c.red + scalar,
00164                  c.green + scalar,
00165                  c.blue + scalar,
00166                  c.alpha + scalar);
00167   }
00168 
00169   Color operator + (Color const& c, float scalar)
00170   {
00171     return scalar + c;
00172   }
00173 
00174   Color operator - (float scalar, Color const& c)
00175   {
00176     return Color(scalar - c.red,
00177                  scalar - c.green,
00178                  scalar - c.blue,
00179                  scalar - c.alpha);
00180   }
00181 
00182 
00183   Color operator - (Color const& c, float scalar)
00184   {
00185     return Color(c.red - scalar,
00186                  c.green - scalar,
00187                  c.blue - scalar,
00188                  c.alpha - scalar);
00189   }
00190 
00191   Color operator * (float scalar, Color const& c)
00192   {
00193     return Color(c.red * scalar,
00194                  c.green * scalar,
00195                  c.blue * scalar,
00196                  c.alpha * scalar);
00197   }
00198 
00199   Color operator * (Color const& c, float scalar)
00200   {
00201     return scalar * c;
00202   }
00203 
00204   // The Hue/Saturation/Value model was created by A. R. Smith in 1978. It is
00205   // based on such intuitive color characteristics as tint, shade and tone (or
00206   // family, purety and intensity). The coordinate system is cylindrical, and
00207   // the colors are defined inside a hexcone.  The hue value H runs from 0 to
00208   // 360║. The saturation S is the degree of strength or purity and is from 0 to
00209   // 1. Purity is how much white is added to the color, so S=1 makes the purest
00210   // color (no white). Brightness V also ranges from 0 to 1, where 0 is the
00211   // black.  There is no transformation matrix for RGB/HSV conversion, but the
00212   // algorithm follows:
00213 
00214   // r,g,b values are from 0 to 1
00215   // h = [0,360], s = [0,1], v = [0,1]
00216   //    if s == 0, then h = -1 (undefined)
00217   void RGBtoHSV(float r, float g, float b, float& h, float& s, float& v)
00218   {
00219     float mini, maxi, delta;
00220 
00221     mini = std::min(std::min(r, g), b);
00222     maxi = std::max(std::max(r, g), b);
00223     v = maxi;
00224 
00225     delta = maxi - mini;
00226 
00227 
00228     if (maxi != 0)
00229     {
00230       s = delta / maxi;
00231     }
00232     else
00233     {
00234       // MAX = 0 (i.e. if v = 0), then s is undefined.  r = g = b = 0
00235       s = 0;
00236       h = -1;
00237       return;
00238     }
00239 
00240     if (delta == 0) // MAX = MIN (i.e. s = 0), then h is undefined. r = g = b
00241     {
00242       h = 0;
00243       s = 0;
00244       return;
00245     }
00246 
00247     if (r == maxi)
00248       h = (g - b) / delta;      // between yellow & magenta
00249     else if (g == maxi)
00250       h = 2 + (b - r) / delta;  // between cyan & yellow
00251     else
00252       h = 4 + (r - g) / delta;  // between magenta & cyan
00253 
00254     h *= 60;  // degrees
00255 
00256     if (h < 0)
00257       h += 360;
00258 
00259     // convert h from [0, 360] to [0, 1]
00260     h = h / 360.0f;
00261 
00262   }
00263 
00264   void HSVtoRGB(float& r, float& g, float& b, float h, float s, float v )
00265   {
00266     int i;
00267     float f, p, q, t;
00268 
00269     // convert h from [0, 1] to [0, 360]
00270     h = h * 360.0f;
00271 
00272     if (s == 0)
00273     {
00274       // achromatic (grey)
00275       r = g = b = v;
00276       return;
00277     }
00278 
00279     h /= 60.0f;         // sector 0 to 5
00280     i = (int) std::floor(h);
00281     f = h - i;          // factorial part of h
00282     p = v * (1 - s);
00283     q = v * (1 - s * f);
00284     t = v * (1 - s * (1 - f));
00285 
00286     switch ( i )
00287     {
00288       case 0:
00289         r = v;
00290         g = t;
00291         b = p;
00292         break;
00293       case 1:
00294         r = q;
00295         g = v;
00296         b = p;
00297         break;
00298       case 2:
00299         r = p;
00300         g = v;
00301         b = t;
00302         break;
00303       case 3:
00304         r = p;
00305         g = q;
00306         b = v;
00307         break;
00308       case 4:
00309         r = t;
00310         g = p;
00311         b = v;
00312         break;
00313       default:    // case 5:
00314         r = v;
00315         g = p;
00316         b = q;
00317         break;
00318     }
00319   }
00320 
00322   // HLS-RGB Conversions //
00324 
00325   // Static function. Auxiliary to HLS2RGB().
00326   static float HLStoRGB_(float rn1, float rn2, float huei)
00327   {
00328     float hue = huei;
00329 
00330     if (hue > 360.0f)
00331       hue = hue - 360.0f;
00332 
00333     if (hue < 0.0f)
00334       hue = hue + 360.0f;
00335 
00336     if (hue < 60.0f )
00337       return rn1 + (rn2 - rn1) * hue / 60.0f;
00338 
00339     if (hue < 180.0f)
00340       return rn2;
00341 
00342     if (hue < 240.0f)
00343       return rn1 + (rn2 - rn1) * (240.0f - hue) / 60.0f;
00344 
00345     return rn1;
00346   }
00347 
00348   void HLStoRGB(float& r, float& g, float& b, float hue, float light, float satur)
00349   {
00350     // Static method to compute RGB from HLS. The l and s are between [0,1]
00351     // and h is between [0, 1]. The returned r,g,b triplet is between [0,1].
00352     hue *= 360.0f;
00353 
00354     float rh, rl, rs, rm1, rm2;
00355     rh = rl = rs = 0;
00356 
00357     if (hue   > 0)
00358       rh = hue;
00359 
00360     if (rh > 360.0f)
00361       rh = 360.0f;
00362 
00363     if (light > 0)
00364       rl = light;
00365 
00366     if (rl > 1)
00367       rl = 1;
00368 
00369     if (satur > 0)
00370       rs = satur;
00371 
00372     if (rs > 1)
00373       rs = 1.0f;
00374 
00375     if (rl <= 0.5f)
00376       rm2 = rl * (1.0f + rs);
00377     else
00378       rm2 = rl + rs - rl * rs;
00379 
00380     rm1 = 2.0f * rl - rm2;
00381 
00382     if (!rs)
00383     {
00384       r = rl;
00385       g = rl;
00386       b = rl;
00387       return;
00388     }
00389 
00390     r = HLStoRGB_(rm1, rm2, rh + 120);
00391     g = HLStoRGB_(rm1, rm2, rh);
00392     b = HLStoRGB_(rm1, rm2, rh - 120);
00393   }
00394 
00395   void RGBtoHLS(float rr, float gg, float bb,
00396                 float& hue, float& light, float& satur)
00397   {
00398     // Static method to compute HLS from RGB. The r,g,b triplet is between
00399     // [0,1], hue is between [0,1], light and satur are [0,1].
00400 
00401     float rnorm, gnorm, bnorm, minval, maxval, msum, mdiff, r, g, b;
00402     r = g = b = 0;
00403 
00404     if (rr > 0)
00405       r = rr;
00406 
00407     if (r > 1)
00408       r = 1;
00409 
00410     if (gg > 0)
00411       g = gg;
00412 
00413     if (g > 1)
00414       g = 1;
00415 
00416     if (bb > 0)
00417       b = bb;
00418 
00419     if (b > 1)
00420       b = 1;
00421 
00422     minval = r;
00423 
00424     if (g < minval)
00425       minval = g;
00426 
00427     if (b < minval)
00428       minval = b;
00429 
00430     maxval = r;
00431 
00432     if (g > maxval)
00433       maxval = g;
00434 
00435     if (b > maxval)
00436       maxval = b;
00437 
00438     rnorm = gnorm = bnorm = 0;
00439     mdiff = maxval - minval;
00440     msum  = maxval + minval;
00441     light = 0.5f * msum;
00442 
00443     if (maxval != minval)
00444     {
00445       rnorm = (maxval - r) / mdiff;
00446       gnorm = (maxval - g) / mdiff;
00447       bnorm = (maxval - b) / mdiff;
00448     }
00449     else
00450     {
00451       satur = hue = 0;
00452       return;
00453     }
00454 
00455     if (light < 0.5f)
00456       satur = mdiff / msum;
00457     else
00458       satur = mdiff / (2.0f - msum);
00459 
00460     if (r == maxval)
00461       hue = 60.0f * (6.0f + bnorm - gnorm);
00462     else if (g == maxval)
00463       hue = 60.0f * (2.0f + rnorm - bnorm);
00464     else
00465       hue = 60.0f * (4.0f + gnorm - rnorm);
00466 
00467     if (hue > 360)
00468       hue = hue - 360;
00469 
00470     hue = hue / 360.0f;
00471   }
00472 
00473   RedGreenBlue::RedGreenBlue(float r, float g, float b)
00474   : red(r)
00475   , green(g)
00476   , blue(b)
00477   {}
00478 
00479   RedGreenBlue::RedGreenBlue(HueSaturationValue const& hsv)
00480   {
00481     HSVtoRGB(red, green, blue, hsv.hue, hsv.saturation, hsv.value);
00482   }
00483 
00484   RedGreenBlue::RedGreenBlue(HueLightnessSaturation const& hls)
00485   {
00486     HLStoRGB(red, green, blue, hls.hue, hls.lightness, hls.saturation);
00487   }
00488 
00489   HueSaturationValue::HueSaturationValue(float h, float s, float v)
00490   : hue(h)
00491   , saturation(s)
00492   , value(v)
00493   {}
00494 
00495   HueSaturationValue::HueSaturationValue(RedGreenBlue const& rgb)
00496   {
00497     RGBtoHSV(rgb.red, rgb.green, rgb.blue, hue, saturation, value);
00498   }
00499 
00500   HueSaturationValue::HueSaturationValue(Color const& c)
00501   {
00502     RGBtoHSV(c.red, c.green, c.blue, hue, saturation, value);
00503   }
00504 
00505   HueLightnessSaturation::HueLightnessSaturation(float h, float l, float s)
00506   : hue(h)
00507   , lightness(l)
00508   , saturation(s)
00509   {}
00510 
00511   HueLightnessSaturation::HueLightnessSaturation(RedGreenBlue const& rgb)
00512   {
00513     RGBtoHLS(rgb.red, rgb.green, rgb.blue, hue, lightness, saturation);
00514   }
00515 
00516   HueLightnessSaturation::HueLightnessSaturation(Color const& c)
00517   {
00518     RGBtoHLS(c.red, c.green, c.blue, hue, lightness, saturation);
00519   }
00520 }
00521 }