[babl] Modify CIE color space conversions to use D50-adapted sRGB



commit 0dba0608bd2cfe69453834c1ec87b7a2be13d2dd
Author: Elle Stone <ellestone ninedegreesbelow com>
Date:   Mon Dec 29 16:42:50 2014 -0500

    Modify CIE color space conversions to use D50-adapted sRGB

 extensions/CIE.c |  988 +++++++++++++++++++-----------------------------------
 1 files changed, 349 insertions(+), 639 deletions(-)
---
diff --git a/extensions/CIE.c b/extensions/CIE.c
index e728f7c..8533cb7 100644
--- a/extensions/CIE.c
+++ b/extensions/CIE.c
@@ -54,6 +54,9 @@ components (void)
   babl_component_new ("CIE b", "chroma", NULL);
   babl_component_new ("CIE C(ab)", "chroma", NULL);
   babl_component_new ("CIE H(ab)", "chroma", NULL);
+  /* babl_component_new ("CIE X", NULL);
+  babl_component_new ("CIE Y", NULL);
+  babl_component_new ("CIE Z", NULL);*/
 }
 
 static void
@@ -88,69 +91,57 @@ models (void)
     babl_component ("CIE H(ab)"),
     babl_component ("A"),
     NULL);
+  /*babl_model_new (
+    "name", "CIE XYZ",
+    babl_component ("CIE X"),
+    babl_component ("CIE Y"),
+    babl_component ("CIE Z"),
+    NULL);*/
 }
 
-/***********    cpercep.h *********   */
+/***********    RGB/CIE color space conversions *********   */
 
-/*
-   Copyright (C) 1997-2002 Adam D. Moss (the "Author").  All Rights Reserved.
+static void  rgbcie_init (void);
 
-   Permission is hereby granted, free of charge, to any person obtaining a copy
-   of this software and associated documentation files (the "Software"), to deal
-   in the Software without restriction, including without limitation the rights
-   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-   copies of the Software, and to permit persons to whom the Software is fur-
-   nished to do so, subject to the following conditions:
-
-   The above copyright notice and this permission notice shall be included in
-   all copies or substantial portions of the Software.
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
-   NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-   AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
-   NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-   Except as contained in this notice, the name of the Author of the
-   Software shall not be used in advertising or otherwise to promote the sale,
-   use or other dealings in this Software without prior written authorization
-   from the Author.
- */
-
-/*
-   cpercep.c: The CPercep Functions v0.9: 2002-02-10
-   Adam D. Moss: adam gimp org <http://www.foxbox.org/adam/code/cpercep/>
-
-   TODO: document functions, rename erroneously-named arguments
- */
-
-static void  cpercep_init (void);
-
-static void  cpercep_rgb_to_space (double  inr,
-                                   double  ing,
-                                   double  inb,
-                                   double *outr,
-                                   double *outg,
-                                   double *outb);
-
-static void  cpercep_space_to_rgb (double  inr,
-                                   double  ing,
-                                   double  inb,
-                                   double *outr,
-                                   double *outg,
-                                   double *outb);
-
-static void  ab_to_chab           (double  a,
+static void  ab_to_CHab           (double  a,
                                    double  b,
-                                   double *C,
-                                   double *H);
+                                   double *to_C,
+                                   double *to_H);
 
-static void  chab_to_ab           (double  C,
+static void  CHab_to_ab           (double  C,
                                    double  H,
-                                   double *a,
-                                   double *b);
-
+                                   double *to_a,
+                                   double *to_b);
+                                   
+static void RGB_to_XYZ            (double R,
+                                   double G,
+                                   double B,
+                                   double *to_X,
+                                   double *to_Y,
+                                   double *to_Z);
+
+static void XYZ_to_LAB            (double X,
+                                   double Y,
+                                   double Z,
+                                   double *to_L,
+                                   double *to_a,
+                                   double *to_b
+                                   );
+
+static void LAB_to_XYZ            (double L,
+                                   double a,
+                                   double b,
+                                   double *to_X,
+                                   double *to_Y,
+                                   double *to_Z
+                                   );
+
+static void XYZ_to_RGB            (double X,
+                                   double Y,
+                                   double Z,
+                                   double *to_R,
+                                   double *to_G,
+                                   double *to_B);
 
 static long
 rgba_to_lab (char *src,
@@ -159,13 +150,16 @@ rgba_to_lab (char *src,
 {
   while (n--)
     {
-      double red   = ((double *) src)[0];
-      double green = ((double *) src)[1];
-      double blue  = ((double *) src)[2];
-
-      double L, a, b;
-
-      cpercep_rgb_to_space (red, green, blue, &L, &a, &b);
+      double R  = ((double *) src)[0];
+      double G  = ((double *) src)[1];
+      double B  = ((double *) src)[2];
+      double X, Y, Z, L, a, b;
+      
+      //convert RGB to XYZ
+      RGB_to_XYZ (R, G, B, &X, &Y, &Z);
+      
+      //convert XYZ to Lab
+      XYZ_to_LAB (X, Y, Z, &L, &a, &b);
 
       ((double *) dst)[0] = L;
       ((double *) dst)[1] = a;
@@ -188,13 +182,16 @@ lab_to_rgba (char *src,
       double a = ((double *) src)[1];
       double b = ((double *) src)[2];
 
-      double red, green, blue;
-
-      cpercep_space_to_rgb (L, a, b, &red, &green, &blue);
-
-      ((double *) dst)[0] = red;
-      ((double *) dst)[1] = green;
-      ((double *) dst)[2] = blue;
+      double X, Y, Z, R, G, B;
+      
+      //convert Lab to XYZ
+      LAB_to_XYZ (L, a, b, &X, &Y, &Z);
+      
+      //convert XYZ to RGB
+      XYZ_to_RGB (X, Y, Z, &R, &G, &B);
+      ((double *) dst)[0] = R;
+      ((double *) dst)[1] = G;
+      ((double *) dst)[2] = B;
       ((double *) dst)[3] = 1.0;
 
       src += sizeof (double) * 3;
@@ -211,14 +208,17 @@ rgba_to_laba (char *src,
 {
   while (n--)
     {
-      double red   = ((double *) src)[0];
-      double green = ((double *) src)[1];
-      double blue  = ((double *) src)[2];
+      double R     = ((double *) src)[0];
+      double G     = ((double *) src)[1];
+      double B     = ((double *) src)[2];
       double alpha = ((double *) src)[3];
-
-      double L, a, b;
-
-      cpercep_rgb_to_space (red, green, blue, &L, &a, &b);
+      double X, Y, Z, L, a, b;
+      
+      //convert RGB to XYZ
+      RGB_to_XYZ (R, G, B, &X, &Y, &Z);
+      
+      //convert XYZ to Lab
+      XYZ_to_LAB (X, Y, Z, &L, &a, &b);
 
       ((double *) dst)[0] = L;
       ((double *) dst)[1] = a;
@@ -243,13 +243,16 @@ laba_to_rgba (char *src,
       double b     = ((double *) src)[2];
       double alpha = ((double *) src)[3];
 
-      double red, green, blue;
-
-      cpercep_space_to_rgb (L, a, b, &red, &green, &blue);
-
-      ((double *) dst)[0] = red;
-      ((double *) dst)[1] = green;
-      ((double *) dst)[2] = blue;
+      double X, Y, Z, R, G, B;
+      
+      //convert Lab to XYZ
+      LAB_to_XYZ (L, a, b, &X, &Y, &Z);
+      
+      //convert XYZ to RGB
+      XYZ_to_RGB (X, Y, Z, &R, &G, &B);
+      ((double *) dst)[0] = R;
+      ((double *) dst)[1] = G;
+      ((double *) dst)[2] = B;
       ((double *) dst)[3] = alpha;
 
       src += sizeof (double) * 4;
@@ -258,8 +261,30 @@ laba_to_rgba (char *src,
   return n;
 }
 
+static void
+CHab_to_ab (double  C,
+            double  H,
+            double *to_a,
+            double *to_b)
+{
+  *to_a = cos (H * RADIANS_PER_DEGREE) * C;
+  *to_b = sin (H * RADIANS_PER_DEGREE) * C;
+}
 
+static void
+ab_to_CHab (double  a,
+            double  b,
+            double *to_C,
+            double *to_H)
+{
+  *to_C = sqrt ( (a * a) + (b * b) );
+  *to_H = atan2 (b, a) * DEGREES_PER_RADIAN;
 
+  // Keep H within the range 0-360
+  if (*to_H < 0.0)
+    *to_H += 360;
+
+}
 
 static long
 rgba_to_lchab (char *src,
@@ -268,13 +293,19 @@ rgba_to_lchab (char *src,
 {
   while (n--)
     {
-      double red   = ((double *) src)[0];
-      double green = ((double *) src)[1];
-      double blue  = ((double *) src)[2];
-      double L, a, b, C, H;
-
-      cpercep_rgb_to_space (red, green, blue, &L, &a, &b);
-      ab_to_chab (a, b, &C, &H);
+      double R = ((double *) src)[0];
+      double G = ((double *) src)[1];
+      double B = ((double *) src)[2];
+      double X, Y, Z, L, a, b, C, H;
+
+      //convert RGB to XYZ
+      RGB_to_XYZ (R, G, B, &X, &Y, &Z);
+      
+      //convert XYZ to Lab
+      XYZ_to_LAB (X, Y, Z, &L, &a, &b);
+      
+      //convert Lab to LCH(ab)
+      ab_to_CHab (a, b, &C, &H);
 
       ((double *) dst)[0] = L;
       ((double *) dst)[1] = C;
@@ -296,14 +327,20 @@ lchab_to_rgba (char *src,
       double L = ((double *) src)[0];
       double C = ((double *) src)[1];
       double H = ((double *) src)[2];
-      double a, b, red, green, blue;
-
-      chab_to_ab (C, H, &a, &b);
-      cpercep_space_to_rgb (L, a, b, &red, &green, &blue);
-
-      ((double *) dst)[0] = red;
-      ((double *) dst)[1] = green;
-      ((double *) dst)[2] = blue;
+      double a, b, X, Y, Z, R, G, B;
+      
+      //Convert LCH(ab) to Lab
+      CHab_to_ab (C, H, &a, &b);
+      
+      //Convert LAB to XYZ
+      LAB_to_XYZ (L, a, b, &X, &Y, &Z);
+      
+      //Convert XYZ to RGB
+      XYZ_to_RGB (X, Y, Z, &R, &G, &B);
+
+      ((double *) dst)[0] = R;
+      ((double *) dst)[1] = G;
+      ((double *) dst)[2] = B;
       ((double *) dst)[3] = 1.0;
 
       src += sizeof (double) * 3;
@@ -320,14 +357,20 @@ rgba_to_lchaba (char *src,
 {
   while (n--)
     {
-      double red   = ((double *) src)[0];
-      double green = ((double *) src)[1];
-      double blue  = ((double *) src)[2];
+      double R = ((double *) src)[0];
+      double G = ((double *) src)[1];
+      double B = ((double *) src)[2];
       double alpha = ((double *) src)[3];
-      double L, a, b, C, H;
+      double X, Y, Z, L, a, b, C, H;
 
-      cpercep_rgb_to_space (red, green, blue, &L, &a, &b);
-      ab_to_chab (a, b, &C, &H);
+      //convert RGB to XYZ
+      RGB_to_XYZ (R, G, B, &X, &Y, &Z);
+      
+      //convert XYZ to Lab
+      XYZ_to_LAB (X, Y, Z, &L, &a, &b);
+      
+      //convert Lab to LCH(ab)
+      ab_to_CHab (a, b, &C, &H);
 
       ((double *) dst)[0] = L;
       ((double *) dst)[1] = C;
@@ -351,14 +394,20 @@ lchaba_to_rgba (char *src,
       double C     = ((double *) src)[1];
       double H     = ((double *) src)[2];
       double alpha = ((double *) src)[3];
-      double a, b, red, green, blue;
-
-      chab_to_ab (C, H, &a, &b);
-      cpercep_space_to_rgb (L, a, b, &red, &green, &blue);
-
-      ((double *) dst)[0] = red;
-      ((double *) dst)[1] = green;
-      ((double *) dst)[2] = blue;
+      double a, b, X, Y, Z, R, G, B;
+      
+      //Convert LCH(ab) to Lab
+      CHab_to_ab (C, H, &a, &b);
+      
+      //Convert Lab to XYZ
+      LAB_to_XYZ (L, a, b, &X, &Y, &Z);
+      
+      //Convert XYZ to RGB
+      XYZ_to_RGB (X, Y, Z, &R, &G, &B);
+      
+      ((double *) dst)[0] = R;
+      ((double *) dst)[1] = G;
+      ((double *) dst)[2] = B;
       ((double *) dst)[3] = alpha;
 
       src += sizeof (double) * 4;
@@ -419,8 +468,20 @@ conversions (void)
     "linear", lchaba_to_rgba,
     NULL
   );
+  /*babl_conversion_new (
+    babl_model ("RGBA"),
+    babl_model ("CIE XYZ"),
+    "linear", RGB_to_XYZ,
+    NULL
+  );
+  babl_conversion_new (
+    babl_model ("CIE XYZ"),
+    babl_model ("RGBA"),
+    "linear", XYZ_to_RGB,
+    NULL
+  );*/
 
-  cpercep_init ();
+  rgbcie_init ();
 }
 
 static void
@@ -435,6 +496,16 @@ formats (void)
     babl_component ("CIE a"),
     babl_component ("CIE b"),
     NULL);
+    
+  /*babl_format_new (
+    "name", "CIE XYZ float",
+    babl_model ("CIE XYZ"),
+
+    babl_type ("float"),
+    babl_component ("CIE X"),
+    babl_component ("CIE Y"),
+    babl_component ("CIE Z"),
+    NULL);*/
 
   babl_format_new (
     "name", "CIE Lab alpha float",
@@ -781,68 +852,6 @@ types (void)
   types_u16 ();
 }
 
-
-
-
-/***********   cpercep.c *********   */
-
-
-/*
-   Copyright (C) 1999-2002 Adam D. Moss (the "Author").  All Rights Reserved.
-
-   Permission is hereby granted, free of charge, to any person obtaining a copy
-   of this software and associated documentation files (the "Software"), to deal
-   in the Software without restriction, including without limitation the rights
-   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-   copies of the Software, and to permit persons to whom the Software is fur-
-   nished to do so, subject to the following conditions:
-
-   The above copyright notice and this permission notice shall be included in
-   all copies or substantial portions of the Software.
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
-   NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-   AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CON-
-   NECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-   Except as contained in this notice, the name of the Author of the
-   Software shall not be used in advertising or otherwise to promote the sale,
-   use or other dealings in this Software without prior written authorization
-   from the Author.
- */
-
-/*
-   cpercep.c: The CPercep Functions v0.9: 2002-02-10
-   Adam D. Moss: adam gimp org <http://www.foxbox.org/adam/code/cpercep/>
-
-   This code module concerns itself with conversion from a hard-coded
-   RGB colour space (sRGB by default) to CIE L*a*b* and back again with
-   (primarily) precision and (secondarily) speed, oriented largely
-   towards the purposes of quantifying the PERCEPTUAL difference between
-   two arbitrary RGB colours with a minimum of fuss.
-
-   Motivation One: The author is disheartened at the amount of graphics
-   processing software around which uses weighted or non-weighted
-   Euclidean distance between co-ordinates within a (poorly-defined) RGB
-   space as the basis of what should really be an estimate of perceptual
-   difference to the human eye.  Certainly it's fast to do it that way,
-   but please think carefully about whether your particular application
-   should be tolerating sloppy results for the sake of real-time response.
-
-   Motivation Two: Lack of tested, re-usable and free code available
-   for this purpose.  The difficulty in finding something similar to
-   CPercep with a free license motivated this project; I hope that this
-   code also serves to illustrate how to perform the
-   R'G'B'->XYZ->L*a*b*->XYZ->R'G'B' transformation correctly since I
-   was distressed to note how many of the equations and code snippets
-   on the net were omitting the reverse transform and/or were using
-   incorrectly-derived or just plain wrong constants.
-
-   TODO: document functions, rename erroneously-named arguments
- */
-
 /* defines added to make it compile outside gimp */
 
 #ifndef gboolean
@@ -859,339 +868,197 @@ types (void)
 /* #include "config.h" */
 #include <math.h>
 
-#ifndef __GLIBC__
-/* cbrt() is a GNU extension */
-#define cbrt(x)    (pow (x, 1.0 / 3.0))
-#endif
-
-
-/* defines:
-
-   SANITY: emits warnings when passed non-sane colours (and usually
-   corrects them) -- useful when debugging.
-
-   APPROX: speeds up the conversion from RGB to the colourspace by
-   assuming that the RGB values passed in are integral and definitely
-   in the range 0->255
- */
-
-/* #define SANITY */
-/* #define APPROX */
-
-
-/* define characteristics of the source RGB space (and the space
-   within which we try to behave linearly). */
-
-/* Phosphor colours: */
-
-/* sRGB/HDTV phosphor colours */
-static const double pxr = 0.64;
-static const double pyr = 0.33;
-static const double pxg = 0.30;
-static const double pyg = 0.60;
-static const double pxb = 0.15;
-static const double pyb = 0.06;
-
-/* White point: */
-
-/* D65 (6500K) (recommended but not a common display default) */
-static const double lxn = 0.312713;
-static const double lyn = 0.329016;
-
-/* D50 (5000K) */
-/*static const double lxn = 0.3457; */
-/*static const double lyn = 0.3585; */
-
-/* D55 (5500K) */
-/*static const double lxn = 0.3324; */
-/*static const double lyn = 0.3474; */
-
-/* D93 (9300K) (a common monitor default, but poor colour reproduction) */
-/* static const double lxn = 0.2848; */
-/* static const double lyn = 0.2932; */
-
-/* illum E (normalized) */
-/*static const double lxn = 1.0/3.0; */
-/*static const double lyn = 1.0/3.0; */
-
-/* illum C (average sunlight) */
-/*static const double lxn = 0.3101; */
-/*static const double lyn = 0.3162; */
-
-/* illum B (direct sunlight) */
-/*static const double lxn = 0.3484; */
-/*static const double lyn = 0.3516; */
-
-/* illum A (tungsten lamp) */
-/*static const double lxn = 0.4476; */
-/*static const double lyn = 0.4074; */
-
-
-static const double LRAMP = 7.99959199;
-
-
-static double xnn, znn;
-
-
-typedef double CMatrix[3][3];
-typedef double CVector[3];
-
-static CMatrix Mrgb_to_xyz, Mxyz_to_rgb;
-
-static int
-Minvert (CMatrix src, CMatrix dest)
-{
-  double det;
-
-  dest[0][0] = src[1][1] * src[2][2] - src[1][2] * src[2][1];
-  dest[0][1] = src[0][2] * src[2][1] - src[0][1] * src[2][2];
-  dest[0][2] = src[0][1] * src[1][2] - src[0][2] * src[1][1];
-  dest[1][0] = src[1][2] * src[2][0] - src[1][0] * src[2][2];
-  dest[1][1] = src[0][0] * src[2][2] - src[0][2] * src[2][0];
-  dest[1][2] = src[0][2] * src[1][0] - src[0][0] * src[1][2];
-  dest[2][0] = src[1][0] * src[2][1] - src[1][1] * src[2][0];
-  dest[2][1] = src[0][1] * src[2][0] - src[0][0] * src[2][1];
-  dest[2][2] = src[0][0] * src[1][1] - src[0][1] * src[1][0];
-
-  det =
-    src[0][0] * dest[0][0] +
-    src[0][1] * dest[1][0] +
-    src[0][2] * dest[2][0];
-
-  if (det <= 0.0)
-    {
-#ifdef SANITY
-      g_printerr ("\n\007 XXXX det: %f\n", det);
-#endif
-      return 0;
-    }
-
-  dest[0][0] /= det;
-  dest[0][1] /= det;
-  dest[0][2] /= det;
-  dest[1][0] /= det;
-  dest[1][1] /= det;
-  dest[1][2] /= det;
-  dest[2][0] /= det;
-  dest[2][1] /= det;
-  dest[2][2] /= det;
-
-  return 1;
-}
-
-
 static void
 rgbxyzrgb_init (void)
 {
-  /* The gamma related code has been removed since we do our
-   * calculations in linear light. To revice that code, use version
-   * control means
-   */
-
-  xnn = lxn / lyn;
-  /* ynn taken as 1.0 */
-  znn = (1.0 - (lxn + lyn)) / lyn;
-
-  {
-    CMatrix MRC, MRCi;
-    double  C1, C2, C3;
-
-    MRC[0][0] = pxr;
-    MRC[0][1] = pxg;
-    MRC[0][2] = pxb;
-    MRC[1][0] = pyr;
-    MRC[1][1] = pyg;
-    MRC[1][2] = pyb;
-    MRC[2][0] = 1.0 - (pxr + pyr);
-    MRC[2][1] = 1.0 - (pxg + pyg);
-    MRC[2][2] = 1.0 - (pxb + pyb);
-
-    Minvert (MRC, MRCi);
-
-    C1 = MRCi[0][0] * xnn + MRCi[0][1] + MRCi[0][2] * znn;
-    C2 = MRCi[1][0] * xnn + MRCi[1][1] + MRCi[1][2] * znn;
-    C3 = MRCi[2][0] * xnn + MRCi[2][1] + MRCi[2][2] * znn;
-
-    Mrgb_to_xyz[0][0] = MRC[0][0] * C1;
-    Mrgb_to_xyz[0][1] = MRC[0][1] * C2;
-    Mrgb_to_xyz[0][2] = MRC[0][2] * C3;
-    Mrgb_to_xyz[1][0] = MRC[1][0] * C1;
-    Mrgb_to_xyz[1][1] = MRC[1][1] * C2;
-    Mrgb_to_xyz[1][2] = MRC[1][2] * C3;
-    Mrgb_to_xyz[2][0] = MRC[2][0] * C1;
-    Mrgb_to_xyz[2][1] = MRC[2][1] * C2;
-    Mrgb_to_xyz[2][2] = MRC[2][2] * C3;
-
-    Minvert (Mrgb_to_xyz, Mxyz_to_rgb);
-  }
 }
 
-
 static void
-xyz_to_rgb (double *inx_outr,
-            double *iny_outg,
-            double *inz_outb)
+RGB_to_XYZ (double R,
+            double G,
+            double B,
+            double *to_X,
+            double *to_Y,
+            double *to_Z)
 {
-  const double x = *inx_outr;
-  const double y = *iny_outg;
-  const double z = *inz_outb;
 
-  *inx_outr = Mxyz_to_rgb[0][0] * x + Mxyz_to_rgb[0][1] * y + Mxyz_to_rgb[0][2] * z;
-  *iny_outg = Mxyz_to_rgb[1][0] * x + Mxyz_to_rgb[1][1] * y + Mxyz_to_rgb[1][2] * z;
-  *inz_outb = Mxyz_to_rgb[2][0] * x + Mxyz_to_rgb[2][1] * y + Mxyz_to_rgb[2][2] * z;
-}
-
-
-static void
-rgb_to_xyz (double *inr_outx,
-            double *ing_outy,
-            double *inb_outz)
-{
-  const double r = *inr_outx;
-  const double g = *ing_outy;
-  const double b = *inb_outz;
+  double RGBtoXYZ[3][3];
+
+/* 
+ * The variables below hard-code the D50-adapted sRGB RGB to XYZ matrix. 
+ *  
+ * In a properly ICC profile color-managed application, this matrix 
+ * is retrieved from the image's ICC profile's RGB colorants. 
+ * 
+ * */
+  RGBtoXYZ[0][0]= 0.43603516;
+  RGBtoXYZ[0][1]= 0.38511658;
+  RGBtoXYZ[0][2]= 0.14305115;
+  RGBtoXYZ[1][0]= 0.22248840;
+  RGBtoXYZ[1][1]= 0.71690369;
+  RGBtoXYZ[1][2]= 0.06060791;
+  RGBtoXYZ[2][0]= 0.01391602;
+  RGBtoXYZ[2][1]= 0.09706116;
+  RGBtoXYZ[2][2]= 0.71392822;
+
+/* Convert RGB to XYZ */
+  *to_X = RGBtoXYZ[0][0]*R + RGBtoXYZ[0][1]*G + RGBtoXYZ[0][2]*B;
+  *to_Y = RGBtoXYZ[1][0]*R + RGBtoXYZ[1][1]*G + RGBtoXYZ[1][2]*B;
+  *to_Z = RGBtoXYZ[2][0]*R + RGBtoXYZ[2][1]*G + RGBtoXYZ[2][2]*B;
 
-  *inr_outx = Mrgb_to_xyz[0][0] * r + Mrgb_to_xyz[0][1] * g + Mrgb_to_xyz[0][2] * b;
-  *ing_outy = Mrgb_to_xyz[1][0] * r + Mrgb_to_xyz[1][1] * g + Mrgb_to_xyz[1][2] * b;
-  *inb_outz = Mrgb_to_xyz[2][0] * r + Mrgb_to_xyz[2][1] * g + Mrgb_to_xyz[2][2] * b;
 }
 
-
-static inline double
-ffunc (const double t)
+static void XYZ_to_RGB (double X,
+                        double Y,
+                        double Z,
+                        double *to_R,
+                        double *to_G,
+                        double *to_B)
 {
-  if (t > 0.008856)
-    {
-      return (cbrt (t));
-    }
-  else
-    {
-      return (7.787 * t + 16.0 / 116.0);
-    }
-}
-
+  double XYZtoRGB[3][3];
+
+/* 
+ * The variables below hard-code the inverse of 
+ * the D50-adapted sRGB RGB to XYZ matrix.
+ * 
+ * In a properly ICC profile color-managed application, 
+ * this matrix is the inverse of the matrix 
+ * retrieved from the image's ICC profile's RGB colorants. 
+ * 
+ * */
+  XYZtoRGB[0][0]=  3.134274799724;
+  XYZtoRGB[0][1]= -1.617275708956;
+  XYZtoRGB[0][2]= -0.490724283042;
+  XYZtoRGB[1][0]= -0.978795575994;
+  XYZtoRGB[1][1]=  1.916161689117;
+  XYZtoRGB[1][2]=  0.033453331711;
+  XYZtoRGB[2][0]=  0.071976988401;
+  XYZtoRGB[2][1]= -0.228984974402;
+  XYZtoRGB[2][2]=  1.405718224383;
+
+/* Convert XYZ to RGB */
+  *to_R = XYZtoRGB[0][0]*X + XYZtoRGB[0][1]*Y + XYZtoRGB[0][2]*Z;
+  *to_G = XYZtoRGB[1][0]*X + XYZtoRGB[1][1]*Y + XYZtoRGB[1][2]*Z;
+  *to_B = XYZtoRGB[2][0]*X + XYZtoRGB[2][1]*Y + XYZtoRGB[2][2]*Z;
 
-static inline double
-ffunc_inv (const double t)
-{
-  if (t > 0.206893)
-    {
-      return (t * t * t);
-    }
-  else
-    {
-      return ((t - 16.0 / 116.0) / 7.787);
-    }
 }
 
-
 static void
-xyz_to_lab (double *inx,
-            double *iny,
-            double *inz)
+XYZ_to_LAB (double X,
+            double Y,
+            double Z,
+            double *to_L,
+            double *to_a,
+            double *to_b
+            )
+            
 {
-  double       L, a, b;
-  double       ffuncY;
-  const double X = *inx;
-  const double Y = *iny;
-  const double Z = *inz;
 
-  if (Y > 0.0)
-    {
-      if (Y > 0.008856)
-        {
-          L = (116.0 * cbrt (Y)) - 16.0;
-        }
-      else
-        {
-          L = (Y * 903.3);
-        }
-
-#ifdef SANITY
-      if (L < 0.0)
-        {
-          g_printerr (" <eek1>%f \007", (float) L);
-        }
-
-      if (L > 100.0)
-        {
-          g_printerr (" <eek2>%f \007", (float) L);
-        }
-#endif
-    }
-  else
-    {
-      L = 0.0;
-    }
+  const double kappa   = 903.3;//24389.0/27.0;
+  const double epsilon = 0.008856;//216/24389.0;
+  
+/* The constants below hard-code the D50-adapted sRGB ICC profile 
+ * reference white, aka the ICC profile D50 illuminant. 
+ * 
+ * In a properly ICC profile color-managed application, the profile 
+ * illuminant values should be retrieved from the image's
+ * ICC profile's illuminant.
+ * 
+ * At present, the ICC profile illuminant is always D50. This might
+ * change when the next version of the ICC specs is released.
+ * 
+ * As encoded in an actual V2 or V4 ICC profile, 
+ * the illuminant values are hexadecimal-rounded, as are the following 
+ * hard-coded D50 ICC profile illuminant values:
+ * 
+ * */  
+  const double X_reference_white = 0.964202880;
+  const double Y_reference_white = 1.000000000;
+  const double Z_reference_white = 0.824905400;
+ 
+  double x_r = X/X_reference_white;
+  double y_r = Y/Y_reference_white;
+  double z_r = Z/Z_reference_white;
+  
+  double f_x, f_y, f_z;
+  
+  if (x_r > epsilon) f_x = pow(x_r, 1.0 / 3.0);
+  else ( f_x = ((kappa * x_r) + 16) / 116.0 );
+  
+  if (y_r > epsilon) f_y = pow(y_r, 1.0 / 3.0);
+  else ( f_y = ((kappa * y_r) + 16) / 116.0 );
+  
+  if (z_r > epsilon) f_z = pow(z_r, 1.0 / 3.0);
+  else ( f_z = ((kappa * z_r) + 16) / 116.0 );
+
+
+  *to_L = (116.0 * f_y) - 16.0;
+  *to_a = 500.0 * (f_x - f_y);
+  *to_b = 200.0 * (f_y - f_z);
 
-  ffuncY = ffunc (Y);
-  a      = 500.0 * (ffunc (X / xnn) - ffuncY);
-  b      = 200.0 * (ffuncY - ffunc (Z / znn));
-
-  *inx = L;
-  *iny = a;
-  *inz = b;
 }
 
-
-static void
-lab_to_xyz (double *inl,
-            double *ina,
-            double *inb)
+static void LAB_to_XYZ (double L,
+                        double a,
+                        double b,
+                        double *to_X,
+                        double *to_Y,
+                        double *to_Z)
 {
-  double       X, Y, Z;
-  double       P;
-  const double L = *inl;
-  const double a = *ina;
-  const double b = *inb;
-
-  if (L > LRAMP)
-    {
-      P = Y = (L + 16.0) / 116.0;
-      Y = Y * Y * Y;
-    }
-  else
-    {
-      Y = L / 903.3;
-      P = 7.787 * Y + 16.0 / 116.0;
-    }
 
-  X = (P + a / 500.0);
-  X = xnn *ffunc_inv (X);
-  Z = (P - b / 200.0);
-  Z = znn *ffunc_inv (Z);
+  const double kappa   = 903.3;//24389.0/27.0;
+  const double epsilon = 0.008856;//216/24389.0;
+  
+  double fy, fx, fz, fx_cubed, fy_cubed, fz_cubed;
+  double xr, yr, zr;
+  
+/* The constants below hard-code the D50-adapted sRGB ICC profile 
+ * reference white, aka the ICC profile D50 illuminant. 
+ * 
+ * In a properly ICC profile color-managed application, the profile 
+ * illuminant values should be retrieved from the image's
+ * ICC profile's illuminant.
+ * 
+ * At present, the ICC profile illuminant is always D50. This might
+ * change when the next version of the ICC specs is released.
+ * 
+ * As encoded in an actual V2 or V4 ICC profile, 
+ * the illuminant values are hexadecimal-rounded, as are the following 
+ * hard-coded D50 ICC profile illuminant values:
+ * 
+ * */
+  const double X_reference_white = 0.964202880;
+  const double Y_reference_white = 1.000000000;
+  const double Z_reference_white = 0.824905400;
+  
+  fy = (L + 16.0) / 116.0;
+  fy_cubed = fy*fy*fy;
+  
+  fz = fy - (b / 200.0);
+  fz_cubed = fz*fz*fz;
+  
+  fx = (a / 500.0) + fy;
+  fx_cubed = fx*fx*fx;
+  
+  if (fx_cubed > epsilon) xr = fx_cubed;
+  else xr = ((116.0 * fx) - 16) / kappa;
+
+  if ( L > (kappa * epsilon) ) yr = fy_cubed;
+  else yr = (L / kappa);
+
+  if (fz_cubed > epsilon) zr = fz_cubed;
+  else zr = ( (116.0 * fz) - 16 ) / kappa;
+  
+  *to_X = xr * X_reference_white;
+  *to_Y = yr * Y_reference_white;
+  *to_Z = zr * Z_reference_white;
 
-#ifdef SANITY
-  if (X < -0.00000)
-    {
-      if (X < -0.0001)
-        g_printerr ("{badX %f {%f,%f,%f}}", X, L, a, b);
-      X = 0.0;
-    }
-  if (Y < -0.00000)
-    {
-      if (Y < -0.0001)
-        g_printerr ("{badY %f}", Y);
-      Y = 0.0;
-    }
-  if (Z < -0.00000)
-    {
-      if (Z < -0.1)
-        g_printerr ("{badZ %f}", Z);
-      Z = 0.0;
-    }
-#endif
-
-  *inl = X;
-  *ina = Y;
-  *inb = Z;
 }
 
 
-
-/* call this before using the CPercep function */
+/* Call this before using the RGB/CIE color space conversions */
 static void
-cpercep_init (void)
+rgbcie_init (void)
 {
   static gboolean initialized = FALSE;
 
@@ -1202,161 +1069,4 @@ cpercep_init (void)
     }
 }
 
-static void
-cpercep_rgb_to_space (double  inr,
-                      double  ing,
-                      double  inb,
-                      double *outr,
-                      double *outg,
-                      double *outb)
-{
-#ifdef APPROX
-#ifdef SANITY
-  /* ADM extra sanity */
-  if ((inr) > 255.0 ||
-      (ing) > 255.0 ||
-      (inb) > 255.0 ||
-      (inr) < -0.0 ||
-      (ing) < -0.0 ||
-      (inb) < -0.0
-  )
-    abort ();
-#endif /* SANITY */
-#endif /* APPROX */
-
-#ifdef SANITY
-  /* ADM extra sanity */
-  if ((inr) > 1.0 ||
-      (ing) > 1.0 ||
-      (inb) > 1.0 ||
-      (inr) < 0.0 ||
-      (ing) < 0.0 ||
-      (inb) < 0.0
-  )
-    {
-      g_printerr ("%%");
-      /* abort(); */
-    }
-#endif /* SANITY */
-
-  rgb_to_xyz (&inr, &ing, &inb);
-
-#ifdef SANITY
-  if (inr < 0.0 || ing < 0.0 || inb < 0.0)
-    {
-      g_printerr (" [BAD2 XYZ: %f,%f,%f]\007 ",
-                  inr, ing, inb);
-    }
-#endif /* SANITY */
-
-  xyz_to_lab (&inr, &ing, &inb);
-
-  *outr = inr;
-  *outg = ing;
-  *outb = inb;
-}
-
-
-static void
-cpercep_space_to_rgb (double  inr,
-                      double  ing,
-                      double  inb,
-                      double *outr,
-                      double *outg,
-                      double *outb)
-{
-  lab_to_xyz (&inr, &ing, &inb);
-
-#ifdef SANITY
-  if (inr < -0.0 || ing < -0.0 || inb < -0.0)
-    {
-      g_printerr (" [BAD1 XYZ: %f,%f,%f]\007 ",
-                  inr, ing, inb);
-    }
-#endif
-
-  xyz_to_rgb (&inr, &ing, &inb);
-
-  *outr = inr;
-  *outg = ing;
-  *outb = inb;
-}
-
-static void
-ab_to_chab (double  a,
-            double  b,
-            double *C,
-            double *H)
-{
-  *C = sqrt (a * a + b * b);
-  *H = atan2 (b, a) * DEGREES_PER_RADIAN;
-
-  /* Keep H within the range 0-360 */
-  if (*H < 0.0)
-    *H += 360;
-}
-
-static void
-chab_to_ab (double  C,
-            double  H,
-            double *a,
-            double *b)
-{
-  *a = cos (H * RADIANS_PER_DEGREE) * C;
-  *b = sin (H * RADIANS_PER_DEGREE) * C;
-}
-
-
-#if 0
-/* EXPERIMENTAL SECTION */
-
-const double
-xscaler (const double start, const double end,
-         const double me, const double him)
-{
-  return start + ((end - start) * him) / (me + him);
-}
-
-
-void
-mix_colours (const double L1, const double a1, const double b1,
-             const double L2, const double a2, const double b2,
-             double *rtnL, double *rtna, double *rtnb,
-             double mass1, double mass2)
-{
-  double w1, w2;
-
-#if 0
-  *rtnL = xscaler (L1, L2, mass1, mass2);
-  *rtna = xscaler (a1, a2, mass1, mass2);
-  *rtnb = xscaler (b1, b2, mass1, mass2);
-#else
-#if 1
-  w1 = mass1 * L1;
-  w2 = mass2 * L2;
-#else
-  w1 = mass1 * (L1 * L1 * L1);
-  w2 = mass2 * (L2 * L2 * L2);
-#endif
-
-  *rtnL = xscaler (L1, L2, mass1, mass2);
-
-  if (w1 <= 0.0 &&
-      w2 <= 0.0)
-    {
-      *rtna   =
-        *rtnb = 0.0;
-#ifdef SANITY
-      /* g_printerr ("\007OUCH. "); */
-#endif
-    }
-  else
-    {
-      *rtna = xscaler (a1, a2, w1, w2);
-      *rtnb = xscaler (b1, b2, w1, w2);
-    }
-#endif
-}
-#endif /* EXPERIMENTAL SECTION */
-
-/***********  /cpercep.c *********   */
+/***********  / RGB/CIE color space conversions *********   */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]