color.h

Engine/source/core/color.h

More...

Classes:

Detailed Description

Public Variables

const F32 gGamma 
const F32 gOneOver255 
const F32 gOneOverGamma 
  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#ifndef _COLOR_H_
 25#define _COLOR_H_
 26
 27#ifndef _MPOINT3_H_
 28#include "math/mPoint3.h" 
 29#endif
 30#ifndef _MPOINT4_H_
 31#include "math/mPoint4.h" 
 32#endif
 33
 34#ifndef _ENGINEAPI_H_
 35#include "console/engineAPI.h"
 36#endif
 37
 38const F32 gGamma = 2.2f;
 39const F32 gOneOverGamma = 1.f / 2.2f;
 40const F32 gOneOver255 = 1.f / 255.f;
 41
 42class ColorI;
 43
 44//32bit color in linear space
 45class LinearColorF
 46{
 47public:
 48   F32 red;
 49   F32 green;
 50   F32 blue;
 51   F32 alpha;
 52
 53public:
 54   LinearColorF() : red(0), green(0), blue(0), alpha(0) {}
 55   LinearColorF(const LinearColorF& in_rCopy);
 56   LinearColorF(const F32 in_r, const F32 in_g, const F32 in_b, const F32 in_a = 1.0f);
 57   LinearColorF(const ColorI &color);
 58   LinearColorF(const char* pStockColorName);
 59
 60   void set( const F32 in_r, const F32 in_g, const F32 in_b, const F32 in_a = 1.0f );
 61   void set( const char* pStockColorName );
 62
 63   static const LinearColorF& StockColor( const char* pStockColorName );
 64   StringTableEntry StockColor( void );
 65
 66   LinearColorF& operator*=(const LinearColorF& in_mul);       // Can be useful for lighting
 67   LinearColorF  operator*(const LinearColorF& in_mul) const;
 68   LinearColorF& operator+=(const LinearColorF& in_rAdd);
 69   LinearColorF  operator+(const LinearColorF& in_rAdd) const;
 70   LinearColorF& operator-=(const LinearColorF& in_rSub);
 71   LinearColorF  operator-(const LinearColorF& in_rSub) const;
 72
 73   LinearColorF& operator*=(const F32 in_mul);
 74   LinearColorF  operator*(const F32 in_mul) const;
 75   LinearColorF& operator/=(const F32 in_div);
 76   LinearColorF  operator/(const F32 in_div) const;
 77
 78   LinearColorF  operator-() const;
 79
 80   bool operator==(const LinearColorF&) const;
 81   bool operator!=(const LinearColorF&) const;
 82
 83   operator F32*() { return &red; }
 84   operator const F32*() const { return &red; }
 85
 86   operator Point3F() const { return Point3F( red, green, blue ); }
 87   operator Point4F() const { return Point4F( red, green, blue, alpha ); }
 88
 89   U32 getARGBPack() const;
 90   U32 getRGBAPack() const;
 91   U32 getABGRPack() const;
 92
 93   void interpolate(const LinearColorF& in_rC1,
 94                    const LinearColorF& in_rC2,
 95                    const F32 in_factor);
 96
 97   bool isClamped() const { return (red >= 0.0f && red <= 1.0f) &&
 98                                   (green >= 0.0f && green <= 1.0f) &&
 99                                   (blue  >= 0.0f && blue  <= 1.0f) &&
100                                   (alpha >= 0.0f && alpha <= 1.0f); }
101   void clamp();
102   
103   //calculate luminance
104   F32 luminance();
105
106   //convert to ColorI - slow operation, avoid when possible
107   ColorI toColorI(const bool keepAsLinear = false);
108   
109   static const LinearColorF ZERO;
110   static const LinearColorF ONE;
111   static const LinearColorF WHITE;
112   static const LinearColorF BLACK;
113   static const LinearColorF RED;
114   static const LinearColorF GREEN;
115   static const LinearColorF BLUE;
116
117   static F32 sSrgbToLinear[256];
118};
119
120
121//8bit color in srgb space
122class ColorI
123{
124public:
125   U8 red;
126   U8 green;
127   U8 blue;
128   U8 alpha;
129
130   struct Hsb
131   {
132      Hsb() :hue(0), sat(0), brightness(0){};
133      Hsb(U32 h, U32 s, U32 b) :hue(h), sat(s), brightness(b){};
134
135      U32 hue;   ///Hue
136      U32 sat;   ///Saturation
137      U32 brightness;   //Brightness/Value/Lightness
138   };
139
140public:
141   ColorI() : red(0), green(0), blue(0), alpha(0) {}
142   ColorI(const ColorI& in_rCopy);
143   ColorI(const Hsb& color);
144   ColorI(const U8 in_r, const U8 in_g, const U8 in_b, const U8 in_a = U8(255));
145   ColorI(const ColorI& in_rCopy, const U8 in_a);
146   ColorI(const char* pStockColorName);
147
148   void set(const Hsb& color);
149
150   void HSLtoRGB_Subfunction(U32& c, const F64& temp1, const F64& temp2, const F64& temp3);
151
152   void set(const String& hex);
153
154   void set(const U8 in_r,
155            const U8 in_g,
156            const U8 in_b,
157            const U8 in_a = U8(255));
158
159   void set(const ColorI& in_rCopy,
160            const U8 in_a);
161
162   void set( const char* pStockColorName );
163
164   static const ColorI& StockColor( const char* pStockColorName );
165   StringTableEntry StockColor( void );
166   
167   bool operator==(const ColorI&) const;
168   bool operator!=(const ColorI&) const;
169   
170   U32 getARGBPack() const;
171   U32 getRGBAPack() const;
172   U32 getABGRPack() const;
173
174   U32 getBGRPack() const;
175   U32 getRGBPack() const;
176
177   U32 getRGBEndian() const;
178   U32 getARGBEndian() const;
179
180   U16 get565()  const;
181   U16 get4444() const;
182
183   Hsb getHSB() const;
184
185   String getHex() const;
186   S32 convertFromHex(const String& hex) const;
187
188   operator const U8*() const { return &red; }
189
190   //convert linear color to srgb - slow operation, avoid when possible
191   ColorI fromLinear();
192   
193   static const ColorI ZERO;
194   static const ColorI ONE;
195   static const ColorI WHITE;
196   static const ColorI BLACK;
197   static const ColorI RED;
198   static const ColorI GREEN;
199   static const ColorI BLUE;
200};
201
202//-----------------------------------------------------------------------------
203
204class StockColorItem
205{
206private:
207   StockColorItem():mColorName("") {}
208
209public:
210   StockColorItem( const char* pName, const U8 red, const U8 green, const U8 blue, const U8 alpha = 255 )
211   {
212      // Sanity!
213      AssertFatal( pName != NULL, "Stock color name cannot be NULL." );
214
215      // Set stock color.
216      // NOTE:-   We'll use the char pointer here.  We can yet use the string-table unfortunately.
217      mColorName = pName;
218      mColorI.set( red, green, blue, alpha );
219      mColorF = mColorI;
220   }
221
222   inline const char*      getColorName( void ) const { return mColorName; }
223   inline const LinearColorF&    getColorF( void ) const { return mColorF; }
224   inline const ColorI&    getColorI( void ) const { return mColorI; }
225
226   const char*         mColorName;
227   LinearColorF              mColorF;
228   ColorI              mColorI;
229};
230
231//-----------------------------------------------------------------------------
232
233class StockColor
234{
235public:
236   static bool isColor( const char* pStockColorName );
237   static const LinearColorF& colorF( const char* pStockColorName );
238   static const ColorI& colorI( const char* pStockColorName );
239   static StringTableEntry name( const LinearColorF& color );
240   static StringTableEntry name( const ColorI& color );
241
242   static S32 getCount( void );
243   static const StockColorItem* getColorItem( const S32 index );
244
245   static void create( void );
246   static void destroy( void );
247};
248
249//------------------------------------------------------------------------------
250//-------------------------------------- INLINES (LinearColorF)
251//
252inline void LinearColorF::set(const F32 in_r, const F32 in_g, const F32 in_b, const F32 in_a)
253{
254   red   = in_r;
255   green = in_g;
256   blue  = in_b;
257   alpha = in_a;
258}
259
260inline LinearColorF::LinearColorF(const LinearColorF& in_rCopy)
261{
262   red   = in_rCopy.red;
263   green = in_rCopy.green;
264   blue  = in_rCopy.blue;
265   alpha = in_rCopy.alpha;
266}
267
268inline LinearColorF::LinearColorF(const F32 in_r, const F32 in_g, const F32 in_b, const F32 in_a)
269{
270   set(in_r, in_g, in_b, in_a);
271}
272
273inline LinearColorF& LinearColorF::operator*=(const LinearColorF& in_mul)
274{
275   red   *= in_mul.red;
276   green *= in_mul.green;
277   blue  *= in_mul.blue;
278   alpha *= in_mul.alpha;
279
280   return *this;
281}
282
283inline LinearColorF LinearColorF::operator*(const LinearColorF& in_mul) const
284{
285   LinearColorF tmp(*this);
286   tmp *= in_mul;
287   return tmp;
288}
289
290inline LinearColorF& LinearColorF::operator+=(const LinearColorF& in_rAdd)
291{
292   red += in_rAdd.red;
293   green += in_rAdd.green;
294   blue += in_rAdd.blue;
295   alpha += in_rAdd.alpha;
296   return *this;
297}
298
299inline LinearColorF LinearColorF::operator+(const LinearColorF& in_rAdd) const
300{
301   LinearColorF temp(*this);
302   temp += in_rAdd;
303   return temp;
304}
305
306inline LinearColorF& LinearColorF::operator-=(const LinearColorF& in_rSub)
307{
308   red -= in_rSub.red;
309   green -= in_rSub.green;
310   blue -= in_rSub.blue;
311   alpha -= in_rSub.alpha;
312   return *this;
313}
314
315inline LinearColorF LinearColorF::operator-(const LinearColorF& in_rSub) const
316{
317   LinearColorF tmp(*this);
318   tmp -= in_rSub;
319   return tmp;
320}
321
322inline LinearColorF& LinearColorF::operator*=(const F32 in_mul)
323{
324   red   *= in_mul;
325   green *= in_mul;
326   blue  *= in_mul;
327   alpha *= in_mul;
328   return *this;
329}
330
331inline LinearColorF LinearColorF::operator*(const F32 in_mul) const
332{
333   LinearColorF tmp(*this);
334   tmp *= in_mul;
335   return tmp;
336}
337
338inline LinearColorF& LinearColorF::operator/=(const F32 in_div)
339{
340   AssertFatal(in_div != 0.0f, "Error, div by zero...");
341   F32 inv = 1.0f / in_div;
342
343   red *= inv;
344   green *= inv;
345   blue *= inv;
346   alpha *= inv;
347   return *this;
348}
349
350inline LinearColorF LinearColorF::operator/(const F32 in_div) const
351{
352   AssertFatal(in_div != 0.0f, "Error, div by zero...");
353   F32 inv = 1.0f / in_div;
354   LinearColorF tmp(*this);
355   tmp /= inv;
356   return tmp;
357}
358
359inline LinearColorF LinearColorF::operator-() const
360{
361   return LinearColorF(-red, -green, -blue, -alpha);
362}
363
364inline bool LinearColorF::operator==(const LinearColorF& in_Cmp) const
365{
366   return (red == in_Cmp.red && green == in_Cmp.green && blue == in_Cmp.blue && alpha == in_Cmp.alpha);
367}
368
369inline bool LinearColorF::operator!=(const LinearColorF& in_Cmp) const
370{
371   return (red != in_Cmp.red || green != in_Cmp.green || blue != in_Cmp.blue || alpha != in_Cmp.alpha);
372}
373
374inline U32 LinearColorF::getARGBPack() const
375{
376   return (U32(alpha * 255.0f + 0.5) << 24) |
377          (U32(red   * 255.0f + 0.5) << 16) |
378          (U32(green * 255.0f + 0.5) <<  8) |
379          (U32(blue  * 255.0f + 0.5) <<  0);
380}
381
382inline U32 LinearColorF::getRGBAPack() const
383{
384   return ( U32( red   * 255.0f + 0.5) <<  0 ) |
385          ( U32( green * 255.0f + 0.5) <<  8 ) |
386          ( U32( blue  * 255.0f + 0.5) << 16 ) |
387          ( U32( alpha * 255.0f + 0.5) << 24 );
388}
389
390inline U32 LinearColorF::getABGRPack() const
391{
392   return (U32(alpha * 255.0f + 0.5) << 24) |
393          (U32(blue  * 255.0f + 0.5) << 16) |
394          (U32(green * 255.0f + 0.5) <<  8) |
395          (U32(red   * 255.0f + 0.5) <<  0);
396
397}
398
399inline void LinearColorF::interpolate(const LinearColorF& in_rC1,
400                    const LinearColorF& in_rC2,
401                    const F32  in_factor)
402{
403   if (in_factor <= 0 || in_rC1 == in_rC2)
404   {
405      red = in_rC1.red;
406      green = in_rC1.green;
407      blue =in_rC1.blue;
408      alpha = in_rC1.alpha;
409      return;
410   }
411   else if (in_factor >= 1)
412   {
413      red = in_rC2.red;
414      green = in_rC2.green;
415      blue = in_rC2.blue;
416      alpha = in_rC2.alpha;
417      return;
418   }
419
420   F32 f2 = 1.0f - in_factor;
421   red = (in_rC1.red   * f2) + (in_rC2.red   * in_factor);
422   green = (in_rC1.green * f2) + (in_rC2.green * in_factor);
423   blue = (in_rC1.blue  * f2) + (in_rC2.blue  * in_factor);
424   alpha = (in_rC1.alpha * f2) + (in_rC2.alpha * in_factor);
425}
426
427inline void LinearColorF::clamp()
428{
429   red = mClampF(red, 0.0f, 1.0f);
430   green = mClampF(green, 0.0f, 1.0f);
431   blue = mClampF(blue, 0.0f, 1.0f);
432   alpha = mClampF(alpha, 0.0f, 1.0f);
433}
434
435inline F32 LinearColorF::luminance()
436{
437   // ITU BT.709
438   //return red * 0.2126f + green * 0.7152f + blue * 0.0722f;
439   // ITU BT.601
440   return red * 0.3f + green * 0.59f + blue * 0.11f;
441}
442
443//------------------------------------------------------------------------------
444//-------------------------------------- INLINES (ColorI)
445//
446inline void ColorI::set(const U8 in_r,
447            const U8 in_g,
448            const U8 in_b,
449            const U8 in_a)
450{
451   red   = in_r;
452   green = in_g;
453   blue  = in_b;
454   alpha = in_a;
455}
456
457inline void ColorI::set(const ColorI& in_rCopy,
458            const U8 in_a)
459{
460   red   = in_rCopy.red;
461   green = in_rCopy.green;
462   blue  = in_rCopy.blue;
463   alpha = in_a;
464}
465
466inline void ColorI::set(const Hsb& color)
467{
468   U32 r = 0;
469   U32 g = 0;
470   U32 b = 0;
471
472   F64 L = ((F64)color.brightness) / 100.0;
473   F64 S = ((F64)color.sat) / 100.0;
474   F64 H = ((F64)color.hue) / 360.0;
475
476   if (color.sat == 0)
477   {
478      r = color.brightness;
479      g = color.brightness;
480      b = color.brightness;
481   }
482   else
483   {
484      F64 temp1 = 0;
485      if (L < 0.50)
486      {
487         temp1 = L*(1 + S);
488      }
489      else
490      {
491         temp1 = L + S - (L*S);
492      }
493
494      F64 temp2 = 2.0*L - temp1;
495
496      F64 temp3 = 0;
497      for (S32 i = 0; i < 3; i++)
498      {
499         switch (i)
500         {
501         case 0: // red
502         {
503            temp3 = H + 0.33333;
504            if (temp3 > 1.0)
505               temp3 -= 1.0;
506            HSLtoRGB_Subfunction(r, temp1, temp2, temp3);
507            break;
508         }
509         case 1: // green
510         {
511            temp3 = H;
512            HSLtoRGB_Subfunction(g, temp1, temp2, temp3);
513            break;
514         }
515         case 2: // blue
516         {
517            temp3 = H - 0.33333;
518            if (temp3 < 0)
519               temp3 += 1;
520            HSLtoRGB_Subfunction(b, temp1, temp2, temp3);
521            break;
522         }
523         default:
524         {
525
526         }
527         }
528      }
529   }
530   red = (U32)((((F64)r) / 100) * 255);
531   green = (U32)((((F64)g) / 100) * 255);
532   blue = (U32)((((F64)b) / 100) * 255);
533   alpha = 255;
534}
535
536// This is a subfunction of HSLtoRGB
537inline void ColorI::HSLtoRGB_Subfunction(U32& c, const F64& temp1, const F64& temp2, const F64& temp3)
538{
539   if ((temp3 * 6.0) < 1.0)
540      c = (U32)((temp2 + (temp1 - temp2)*6.0*temp3)*100.0);
541   else
542      if ((temp3 * 2.0) < 1.0)
543         c = (U32)(temp1*100.0);
544      else
545         if ((temp3 * 3.0) < 2.0)
546            c = (U32)((temp2 + (temp1 - temp2)*(0.66666 - temp3)*6.0)*100.0);
547         else
548            c = (U32)(temp2*100.0);
549   return;
550}
551
552inline void ColorI::set(const String& hex)
553{
554   String redString;
555   String greenString;
556   String blueString;
557
558   //if the prefix # was attached to hex
559   if (hex[0] == '#')
560   {
561      redString = hex.substr(1, 2);
562      greenString = hex.substr(3, 2);
563      blueString = hex.substr(5, 2);
564   }
565   else
566   {
567      // since there is no prefix attached to hex
568      redString = hex.substr(0, 2);
569      greenString = hex.substr(2, 2);
570      blueString = hex.substr(4, 2);
571   }
572
573   red = (U8)(convertFromHex(redString));
574   green = (U8)(convertFromHex(greenString));
575   blue = (U8)(convertFromHex(blueString));
576}
577
578inline S32 ColorI::convertFromHex(const String& hex) const
579{
580   S32 hexValue = 0;
581
582   S32 a = 0;
583   S32 b = hex.length() - 1;
584
585   for (; b >= 0; a++, b--)
586   {
587      if (hex[b] >= '0' && hex[b] <= '9')
588      {
589         hexValue += (hex[b] - '0') * (1 << (a * 4));
590      }
591      else
592      {
593         switch (hex[b])
594         {
595         case 'A':
596         case 'a':
597            hexValue += 10 * (1 << (a * 4));
598            break;
599
600         case 'B':
601         case 'b':
602            hexValue += 11 * (1 << (a * 4));
603            break;
604
605         case 'C':
606         case 'c':
607            hexValue += 12 * (1 << (a * 4));
608            break;
609
610         case 'D':
611         case 'd':
612            hexValue += 13 * (1 << (a * 4));
613            break;
614
615         case 'E':
616         case 'e':
617            hexValue += 14 * (1 << (a * 4));
618            break;
619
620         case 'F':
621         case 'f':
622            hexValue += 15 * (1 << (a * 4));
623            break;
624
625         default:
626            Con::errorf("Error, invalid character '%c' in hex number", hex[a]);
627            break;
628         }
629      }
630   }
631
632   return hexValue;
633}
634
635inline ColorI::ColorI(const ColorI& in_rCopy)
636{
637   red   = in_rCopy.red;
638   green = in_rCopy.green;
639   blue  = in_rCopy.blue;
640   alpha = in_rCopy.alpha;
641}
642
643inline ColorI::ColorI(const Hsb& color)
644{
645   set(color);
646}
647
648inline ColorI::ColorI(const U8 in_r,
649               const U8 in_g,
650               const U8 in_b,
651               const U8 in_a)
652{
653   set(in_r, in_g, in_b, in_a);
654}
655
656inline ColorI::ColorI(const ColorI& in_rCopy,
657                      const U8 in_a)
658{
659   set(in_rCopy, in_a);
660}
661
662inline bool ColorI::operator==(const ColorI& in_Cmp) const
663{
664   return (red == in_Cmp.red && green == in_Cmp.green && blue == in_Cmp.blue && alpha == in_Cmp.alpha);
665}
666
667inline bool ColorI::operator!=(const ColorI& in_Cmp) const
668{
669   return (red != in_Cmp.red || green != in_Cmp.green || blue != in_Cmp.blue || alpha != in_Cmp.alpha);
670}
671
672inline U32 ColorI::getARGBPack() const
673{
674   return (U32(alpha) << 24) |
675          (U32(red)   << 16) |
676          (U32(green) <<  8) |
677          (U32(blue)  <<  0);
678}
679
680inline U32 ColorI::getRGBAPack() const
681{
682   return ( U32( red )   <<  0 ) |
683          ( U32( green ) <<  8 ) |
684          ( U32( blue )  << 16 ) |
685          ( U32( alpha ) << 24 );
686}
687
688inline U32 ColorI::getABGRPack() const
689{
690   return (U32(alpha) << 24) |
691          (U32(blue)  << 16) |
692          (U32(green) <<  8) |
693          (U32(red)   <<  0);
694}
695
696
697inline U32 ColorI::getBGRPack() const
698{
699   return (U32(blue)  << 16) |
700          (U32(green) <<  8) |
701          (U32(red)   <<  0);
702}
703
704inline U32 ColorI::getRGBPack() const
705{
706   return (U32(red)   << 16) |
707          (U32(green) <<  8) |
708          (U32(blue)  <<  0);
709}
710
711inline U32 ColorI::getRGBEndian() const
712{
713#if defined(TORQUE_BIG_ENDIAN)
714      return(getRGBPack());
715#else
716      return(getBGRPack());
717#endif
718}
719
720inline U32 ColorI::getARGBEndian() const
721{
722#if defined(TORQUE_BIG_ENDIAN)
723   return(getABGRPack());
724#else
725   return(getARGBPack());
726#endif
727}
728
729inline U16 ColorI::get565() const
730{
731   return U16((U16(red   >> 3) << 11) |
732              (U16(green >> 2) <<  5) |
733              (U16(blue  >> 3) <<  0));
734}
735
736inline U16 ColorI::get4444() const
737{
738   return U16(U16(U16(alpha >> 4) << 12) |
739              U16(U16(red   >> 4) <<  8) |
740              U16(U16(green >> 4) <<  4) |
741              U16(U16(blue  >> 4) <<  0));
742}
743
744inline ColorI::Hsb ColorI::getHSB() const
745{
746   F64 rPercent = ((F64)red) / 255;
747   F64 gPercent = ((F64)green) / 255;
748   F64 bPercent = ((F64)blue) / 255;
749
750   F64 maxColor = 0.0;
751   if ((rPercent >= gPercent) && (rPercent >= bPercent))
752      maxColor = rPercent;
753   if ((gPercent >= rPercent) && (gPercent >= bPercent))
754      maxColor = gPercent;
755   if ((bPercent >= rPercent) && (bPercent >= gPercent))
756      maxColor = bPercent;
757
758   F64 minColor = 0.0;
759   if ((rPercent <= gPercent) && (rPercent <= bPercent))
760      minColor = rPercent;
761   if ((gPercent <= rPercent) && (gPercent <= bPercent))
762      minColor = gPercent;
763   if ((bPercent <= rPercent) && (bPercent <= gPercent))
764      minColor = bPercent;
765
766   F64 H = 0.0;
767   F64 S = 0.0;
768   F64 B = 0.0;
769
770   B = (maxColor + minColor) / 2.0;
771
772   if (maxColor == minColor)
773   {
774      H = 0.0;
775      S = 0.0;
776   }
777   else
778   {
779      if (B < 0.50)
780      {
781         S = (maxColor - minColor) / (maxColor + minColor);
782      }
783      else
784      {
785         S = (maxColor - minColor) / (2.0 - maxColor - minColor);
786      }
787      if (maxColor == rPercent)
788      {
789         H = (gPercent - bPercent) / (maxColor - minColor);
790      }
791      if (maxColor == gPercent)
792      {
793         H = 2.0 + (bPercent - rPercent) / (maxColor - minColor);
794      }
795      if (maxColor == bPercent)
796      {
797         H = 4.0 + (rPercent - gPercent) / (maxColor - minColor);
798      }
799   }
800
801   ColorI::Hsb val;
802   val.sat = (U32)(S * 100);
803   val.brightness = (U32)(B * 100);
804   H = H*60.0;
805   if (H < 0.0)
806      H += 360.0;
807   val.hue = (U32)H;
808
809   return val;
810}
811
812inline String ColorI::getHex() const
813{
814   char r[255];
815   dSprintf(r, sizeof(r), "%.2X", red);
816   String result(r);
817
818   char g[255];
819   dSprintf(g, sizeof(g), "%.2X", green);
820   result += g;
821
822   char b[255];
823   dSprintf(b, sizeof(b), "%.2X", blue);
824   result += b;
825
826   return result;
827}
828
829inline LinearColorF::LinearColorF( const ColorI &color)
830{
831   red = sSrgbToLinear[color.red],
832   green = sSrgbToLinear[color.green],
833   blue = sSrgbToLinear[color.blue],
834   alpha = F32(color.alpha * gOneOver255);
835}
836
837inline ColorI LinearColorF::toColorI(const bool keepAsLinear)
838{
839   if (isClamped())
840   {
841      if (keepAsLinear)
842      {
843         return ColorI(U8(red * 255.0f + 0.5), U8(green * 255.0f + 0.5), U8(blue * 255.0f + 0.5), U8(alpha * 255.0f + 0.5));
844      }
845      else
846      {
847   #ifdef TORQUE_USE_LEGACY_GAMMA
848         float r = mPow(red, gOneOverGamma);
849         float g = mPow(green, gOneOverGamma);
850         float b = mPow(blue, gOneOverGamma);
851         return ColorI(U8(r * 255.0f + 0.5), U8(g * 255.0f + 0.5), U8(b * 255.0f + 0.5), U8(alpha * 255.0f + 0.5));
852   #else
853         float r = red < 0.0031308f ? 12.92f * red : 1.055 * mPow(red, 1.0f / 2.4f) - 0.055f;
854         float g = green < 0.0031308f ? 12.92f * green : 1.055 * mPow(green, 1.0f / 2.4f) - 0.055f;
855         float b = blue < 0.0031308f ? 12.92f * blue : 1.055 * mPow(blue, 1.0f / 2.4f) - 0.055f;
856         return ColorI(U8(r * 255.0f + 0.5), U8(g * 255.0f + 0.5), U8(b * 255.0f + 0.5), U8(alpha * 255.0f + 0.5));
857   #endif
858      }
859   }
860   else
861   {
862      LinearColorF color = LinearColorF(*this);
863      color.clamp();
864
865      if (keepAsLinear)
866      {
867         return ColorI(U8(color.red * 255.0f + 0.5), U8(color.green * 255.0f + 0.5), U8(color.blue * 255.0f + 0.5), U8(color.alpha * 255.0f + 0.5));
868      }
869      else
870      {
871   #ifdef TORQUE_USE_LEGACY_GAMMA
872         float r = mPow(red, gOneOverGamma);
873         float g = mPow(green, gOneOverGamma);
874         float b = mPow(blue, gOneOverGamma);
875         return ColorI(U8(r * 255.0f + 0.5), U8(g * 255.0f + 0.5), U8(b * 255.0f + 0.5), U8(alpha * 255.0f + 0.5));
876   #else
877         float r = red < 0.0031308f ? 12.92f * red : 1.055 * mPow(red, 1.0f / 2.4f) - 0.055f;
878         float g = green < 0.0031308f ? 12.92f * green : 1.055 * mPow(green, 1.0f / 2.4f) - 0.055f;
879         float b = blue < 0.0031308f ? 12.92f * blue : 1.055 * mPow(blue, 1.0f / 2.4f) - 0.055f;
880         return ColorI(U8(r * 255.0f + 0.5), U8(g * 255.0f + 0.5), U8(b * 255.0f + 0.5), U8(alpha * 255.0f + 0.5));
881   #endif
882      }
883   }
884}
885
886inline ColorI ColorI::fromLinear()
887{
888   //manually create LinearColorF, otherwise it will try and convert to linear first
889   LinearColorF linearColor = LinearColorF(F32(red) * gOneOver255,
890                                           F32(green) * gOneOver255,
891                                           F32(blue) * gOneOver255,
892                                           F32(alpha) * gOneOver255);
893   //convert back to srgb
894   return linearColor.toColorI();
895}
896
897#endif //_COLOR_H_
898