gml_HLSColor.h

00001 // gml_HLSColor.h --
00002 // 
00003 //  Defines the gml_TColor_HLS class template
00004 // 
00005 // Copyright (c) 2004 CLIPS-IMAG
00006 // 
00007 // See the file "gml_LicenseTerms.txt" for information on usage and redistribution
00008 // of this file, and for a DISCLAIMER OF ALL WARRANTIES.
00009 // 
00010 //  Created on January 9, 2004 (JL).
00011 
00012 #ifndef __GML_HLSCOLOR__
00013 #define __GML_HLSCOLOR__
00014 
00015 #include <float.h>
00016 #include <assert.h>
00017 #include "gml/base/gml_Types.h"
00018 #include "gml/base/gml_Attributes.h"
00019 #include "gml/math/gml_Math.h"
00020 #include "gml/math/gml_Tables.h"
00021 
00022 ///
00023 /// gml_TColor_HLS_FFF --
00024 ///   A union type used to represent HLS-encoded color pixels.
00025 ///   HLS is Metrick's Hue, Lightness, Saturation colorspace.
00026 ///
00027 ///   Some of this code is adapted from section 15.3.5 of "Computer Graphics:
00028 ///   Principles and Practice", by J.D. Foley and A. van Dam.
00029 ///
00030 class gml_TColor_HLS_FFF
00031 {
00032 public:
00033   ///
00034   /// Constructor --
00035   ///   Convert a 24-bit RGB triplet into HLS and fill in the object.
00036   ///
00037   gml_TColor_HLS_FFF (UInt8 const r, UInt8 const g, UInt8 const b) {
00038     Float32 fr =    Float32 (r) * 0.00392156862745F;       // that's 1/255
00039     Float32 fg =    Float32 (g) * 0.00392156862745F;
00040     Float32 fb =    Float32 (b) * 0.00392156862745F;
00041     Float32 min =   Min (fr, fg, fb);
00042     Float32 max =   Max (fr, fg, fb);
00043     Float32 delta = max - min;
00044     Float32 sum =   max + min;
00045     Float32 h, l, s;
00046 
00047     l = 0.5F * sum;
00048 
00049     if(delta < FLT_EPSILON) {                           // achromatic case
00050       s = 0.0F;
00051       h = 0.0F;
00052     } else {
00053       s = (l <= 0.5F) ? Divide (delta, sum) : Divide (delta, 2.0F - sum);
00054 
00055       if (fr == max)      h =        Divide (fg - fb, delta);
00056       else if (fg == max) h = 2.0F + Divide (fb - fr, delta);
00057       else /*fb == max*/  h = 4.0F + Divide (fr - fg, delta);
00058     }
00059 
00060     H = fmodf (0.166666666667 * h + 1.0F, 1.0F);
00061     L = l;
00062     S = s;
00063 
00064     assert (H >= 0.0F && H <= 1.0F);
00065     assert (L >= 0.0F && L <= 1.0F);
00066     assert (S >= 0.0F && S <= 1.0F);
00067   }
00068 
00069   ///
00070   /// Constructor --
00071   ///   Fill the structure from data in memory
00072   ///
00073   gml_TColor_HLS_FFF (const void *const ptr) {
00074     *this = *((gml_TColor_HLS_FFF *) ptr);
00075   }
00076 
00077   ///
00078   /// Constructor --
00079   ///   Leave the structure `as is'.
00080   ///             I.e. probably zero.
00081   ///
00082   gml_TColor_HLS_FFF () {}
00083 
00084   ///
00085   /// Print --
00086   ///   Dump a representation of the pixel to stdout: channels in hex and
00087   ///   in degrees, percent, percent format.
00088   ///   @warning no newline appended !
00089   ///
00090   void Print () {
00091     printf ("%f %f %f (%3d %3d%% %3d%%)", H, L, S,
00092       (int) LRound (fmod (360.0 * H + 360.0, 360.0)),
00093       (int) LRound (100 * L), (int) LRound (100 * S)
00094   );
00095   }
00096 
00097   ///
00098   /// Store --
00099   ///   Place the structure's data in memory
00100   ///
00101   void Store (void *const ptr) {
00102     *((gml_TColor_HLS_FFF *) ptr) = *this;
00103   }
00104 
00105   ///
00106   /// Convert --
00107   ///   Convert the HLS data in this object to a 24 bit RGB triplet.
00108   ///
00109   void Convert (UInt8 &r, UInt8 &g, UInt8 &b) {
00110     Float32 fr, fg, fb;
00111 
00112     Float32 m2 = (L <= 0.5F) ? (L + L * S) : (L + S - L * S);
00113     Float32 m1 = 2.0F * L - m2;
00114 
00115     if (S == 0.0F) {
00116       fr = fg = fb = L;
00117     } else {
00118       fr = Value (m1, m2, H + 0.333333333333F);
00119       fg = Value (m1, m2, H);
00120       fb = Value (m1, m2, H - 0.333333333333F);
00121     }
00122 
00123     assert (fr <= 1.0F + FLT_EPSILON && fr >= 0.0F - FLT_EPSILON);
00124     assert (fg <= 1.0F + FLT_EPSILON && fg >= 0.0F - FLT_EPSILON);
00125     assert (fb <= 1.0F + FLT_EPSILON && fb >= 0.0F - FLT_EPSILON);
00126     r = (UInt8) LRound (fr * 255.0F);
00127     g = (UInt8) LRound (fg * 255.0F);
00128     b = (UInt8) LRound (fb * 255.0F);
00129   }
00130 
00131   ///
00132   /// Distance --
00133   ///   Compute the 16-bit distance to another HLS pixel.
00134   ///   The euclidian distance in the hue, saturation disc is used.
00135   ///
00136   UInt16 Distance (const gml_TColor_HLS_FFF other)
00137   {
00138     const Float32 pi = 3.14159265358979323846F;
00139     Float32 x0, x1, y0, y1, res;
00140 
00141     x0 = S * Cos (2.0F * pi * H);
00142     y0 = S * Sin (2.0F * pi * H);
00143     x1 = other.S * Cos (2.0F * pi * other.H);
00144     y1 = other.S * Sin (2.0F * pi * other.H);
00145     res = Min (1.0F, Norm (x0, y0, x1, y1));
00146     assert (res >= 0.0F && res <= 1.0F);
00147     return (UInt16) LFloor (res * 65535.0F);
00148   }
00149 
00150   struct {                          ///< structure for transparent channel access
00151     #if BYTE_ORDER == BIG_ENDIAN
00152   Float32 H;                  ///< hue channel
00153   Float32 L;                  ///< saturation channel
00154   Float32 S;                  ///< value channel
00155     #else
00156   Float32 S;                  ///< value channel
00157   Float32 L;                  ///< saturation channel
00158   Float32 H;                  ///< hue channel
00159     #endif
00160   } GML_PACKED;
00161 
00162 private:
00163 
00164   /// Helper for Convert()
00165   inline static Float32 Value (Float32 n1, Float32 n2, Float32 hue)
00166   {
00167     hue = fmodf (hue + 1.0F, 1.0F);
00168     if (hue < 0.166666666667F) {
00169       return n1 + (n2 - n1) * hue * 6.0F;
00170     } else if (hue < 0.5F) {
00171       return n2;
00172     } else if (hue < 0.666666666667F) {
00173       return n1 + (n2 - n1) * (0.666666666667F - hue) * 6.0F;
00174     } else {
00175       return n1;
00176     }
00177   }
00178 
00179 };
00180 
00181 ///
00182 /// gml_TColor_HLS --
00183 ///   A union type used to represent HLS-encoded color pixels.
00184 ///   HLS is Metrick's Hue, Lightness, Saturation colorspace.
00185 ///   The type (and bitwidth) used for each channel is the template parameter.
00186 ///
00187 template <typename Channel> class gml_TColor_HLS {
00188 public:
00189   ///
00190   /// Constructor --
00191   ///   Convert a 24-bit RGB triplet into HLS and fill in the object.
00192   ///
00193   gml_TColor_HLS (UInt8 const r, UInt8 const g, UInt8 const b) {
00194     UInt8 max, min;                 // max and min RGB values
00195     SInt32 h;
00196 
00197     max = Max (r, g, b);
00198     min = Min (r, g, b);
00199     #ifndef __GML_AVOID_EUCLIDIAN_DIVISION
00200       L = ((max + min) * sChannelMax + 255) / (2 * 255);
00201     #else
00202       L = (((max + min) * sChannelMax) * 257ULL + (1<<16)) >> 17;
00203     #endif
00204 
00205     // achromatic case
00206     if (max == min) {
00207       S = H = 0;
00208       return;
00209     }
00210     
00211     // chromatic case
00212     SInt32 dr, dg, db;              // intermediate values: % of spread from max
00213     UInt32 dif = max-min;
00214     UInt32 sum = Select (UInt32(L), UInt32(sChannelMax / 2), UInt32(max+min), UInt32(2*255 - (max+min)));
00215     #ifndef __GML_AVOID_EUCLIDIAN_DIVISION
00216       S = ((dif * sChannelMax) + (sum / 2)) / sum;
00217 
00218       dr = (((max - r) * (sChannelMax / 6)) + (dif / 2)) / dif;
00219       dg = (((max - g) * (sChannelMax / 6)) + (dif / 2)) / dif;
00220       db = (((max - b) * (sChannelMax / 6)) + (dif / 2)) / dif;
00221     #else
00222       UInt64 rdif_mnt;    // mantissa of dif's reciprocate
00223       UInt64 rsum_mnt;    // mantissa of sum's reciprocate
00224       
00225       rdif_mnt = ReciprocalMantissa_15_10bit (dif);
00226       rsum_mnt = ReciprocalMantissa_15_10bit (sum);
00227 
00228       S = (((dif * sChannelMax) + (sum / 2)) * rsum_mnt + (1<<14)) >> 15;
00229 
00230       dr = ((((max - r) * (sChannelMax / 6)) + (dif / 2)) * rdif_mnt + (1<<14)) >> 15;
00231       dg = ((((max - g) * (sChannelMax / 6)) + (dif / 2)) * rdif_mnt + (1<<14)) >> 15;
00232       db = ((((max - b) * (sChannelMax / 6)) + (dif / 2)) * rdif_mnt + (1<<14)) >> 15;
00233     #endif
00234   
00235     if (r == max)
00236       h = db - dg;
00237     else if (g == max)
00238       h = (sChannelMax / 3) + dr - db;
00239     else /* b == max */
00240       h = ((2 * sChannelMax) / 3) + dg - dr;
00241 
00242     if (h < 0)                          H = h + sChannelMax;
00243     else if ((UInt32) h > sChannelMax)  H = h - sChannelMax;
00244     else                                H = h;
00245   }
00246 
00247   ///
00248   /// Constructor --
00249   ///   Fill the structure from data in memory
00250   ///
00251   gml_TColor_HLS (const void *const ptr)
00252   {
00253     *this = *((gml_TColor_HLS < Channel > *)ptr);
00254   }
00255 
00256   ///
00257   /// Constructor --
00258   ///   Leave the structure `as is'. I.e. probably zero.
00259   ///
00260   gml_TColor_HLS () { }
00261 
00262   ///
00263   /// Print --
00264   ///   Dump a representation of the pixel to stdout: channels in hex and
00265   ///   in degrees, percent, percent format.
00266   ///   @warning no newline appended !
00267   ///
00268   void Print () 
00269   {
00270     printf ("% 8X % 8X % 8X (%3d %3d%% %3d%%)", H, L, S,
00271       (UInt32) round (fmod (360.0 * H / sChannelMax + 360.0, 360.0)),
00272       100 * L / sChannelMax, 100 * S / sChannelMax);
00273   }
00274 
00275   ///
00276   /// Store --
00277   ///   Place the structure's data in memory
00278   ///
00279   void Store (void *const ptr)
00280   {
00281     *((gml_TColor_HLS<Channel> *) ptr) = *this;
00282   }
00283 
00284   ///
00285   /// Convert --
00286   ///   Convert the HLS data in this object to a 24 bit RGB triplet.
00287   ///
00288   void Convert (UInt8 &r, UInt8 &g, UInt8 &b)
00289   {
00290     SInt32 R, G, B;       // RGB component values
00291     SInt32 m1, m2;        // magic numbers (really!)
00292 
00293     if (S == 0) {               /* achromatic case */
00294       R = G = B = (L * 255) >> (sChannelBits-1);
00295     } else {                      /* chromatic case */
00296       // set up magic numbers 
00297       if (L <= (sChannelMax / 2))
00298   m2 = (L * (sChannelMax + S) + (sChannelMax / 2)) >> (sChannelBits-1);
00299       else
00300   m2 = L + S - (((L * S) + (sChannelMax / 2)) >> (sChannelBits-1));
00301       m1 = 2 * L - m2;
00302 
00303       // get RGB, change units from sChannelMax to 255 
00304       R = (HueToRGB (m1, m2, H + (sChannelMax / 3)) * 255 + (sChannelMax / 2)) >> (sChannelBits-1);
00305       G = (HueToRGB (m1, m2, H                    ) * 255 + (sChannelMax / 2)) >> (sChannelBits-1);
00306       B = (HueToRGB (m1, m2, H - (sChannelMax / 3)) * 255 + (sChannelMax / 2)) >> (sChannelBits-1);
00307     }
00308     
00309     assert (R >=0 && R <= 255);
00310     assert (G >=0 && G <= 255);
00311     assert (B >=0 && B <= 255);
00312     r = R;
00313     g = G;
00314     b = B;
00315   }
00316 
00317   ///
00318   /// Distance --
00319   ///   Compute the 16-bit distance to another HLS pixel.
00320   ///   The euclidian distance in the hue, saturation disc is used.
00321   ///
00322   UInt16 Distance (const gml_TColor_HLS<Channel> other)
00323   {
00324     gml_TColor_HLS_FFF pix0, pix1;
00325     pix0.H = Divide (H, sChannelMax);
00326     pix0.L = Divide (L, sChannelMax);
00327     pix0.S = Divide (S, sChannelMax);
00328     pix1.H = Divide (other.H, other.sChannelMax);
00329     pix1.L = Divide (other.L, other.sChannelMax);
00330     pix1.S = Divide (other.S, other.sChannelMax);
00331     return pix0.Distance (pix1);
00332   }
00333 
00334   static UInt8 const sChannelBits = 8 * sizeof (Channel);       ///< bits per channel
00335   static UInt64 const sChannelMax = (1 << (sChannelBits-1));    ///< max channel value
00336 //  static UInt64 const sChannelMax = (1 << sChannelBits) - 1;    ///< max channel value
00337 
00338   struct {                          ///< structure for transparent channel access
00339     #if BYTE_ORDER == BIG_ENDIAN
00340   Channel H;                  ///< hue channel
00341   Channel L;                  ///< saturation channel
00342   Channel S;                  ///< value channel
00343     #else
00344   Channel S;                  ///< value channel
00345   Channel L;                  ///< saturation channel
00346   Channel H;                  ///< hue channel
00347     #endif
00348   } GML_PACKED;
00349 
00350 private:
00351   inline static
00352   SInt32 HueToRGB (SInt32 n1, SInt32 n2, SInt32 hue) {
00353     // range check: note values passed add/subtract thirds of range 
00354     if (hue < 0)            hue += sChannelMax;
00355     if (hue > SInt32(sChannelMax)) 
00356           hue -= sChannelMax;
00357 
00358     // return r,g, or b value from this tridrant 
00359     if (hue < SInt32(sChannelMax / 6))
00360       return (n1 + (((n2 - n1) * hue + (sChannelMax / 12)) / (sChannelMax / 6)));
00361     if (hue < SInt32(sChannelMax / 2))
00362       return (n2);
00363     if (hue < SInt32((sChannelMax * 2) / 3))
00364       return (n1 + (((n2 - n1) * (((sChannelMax * 2) / 3) - hue) + (sChannelMax / 12)) / (sChannelMax / 6)));
00365     else
00366       return (n1);
00367   }
00368 
00369 };
00370 
00371 //
00372 // gml_TColor_HLS_888, gml_TColor_HLS_SSS, gml_TColor_HLS_LLL --
00373 //
00374 typedef gml_TColor_HLS <UInt8>gml_TColor_HLS_888;
00375 typedef gml_TColor_HLS <UInt16>gml_TColor_HLS_SSS;
00376 typedef gml_TColor_HLS <UInt32>gml_TColor_HLS_LLL;
00377 
00378 #endif /* __GML_HLSCOLOR__ */
00379 
Generated on Tue Jun 12 14:03:27 2007 for gml by Doxygen 1.5.2.