diff options
| author | Mark Oteiza | 2017-09-21 22:47:24 -0400 |
|---|---|---|
| committer | Mark Oteiza | 2017-09-21 23:06:00 -0400 |
| commit | d24ec5854098841388dfecf2c668e7f48f348af0 (patch) | |
| tree | f8abf85b73a971d981874daa9876aa0640403ab3 /src | |
| parent | a81d5a3d3fcb76f6b074c2c721b80b1802135d41 (diff) | |
| download | emacs-d24ec5854098841388dfecf2c668e7f48f348af0.tar.gz emacs-d24ec5854098841388dfecf2c668e7f48f348af0.zip | |
Expose viewing conditions in CAM02-UCS metric
Also add tests from the colorspacious library. Finally, catch an
errant calculation, where degrees were not being converted to radians.
* src/lcms.c (deg2rad, default_viewing_conditions):
(parse_viewing_conditions): New functions.
(lcms-cam02-ucs): Add comments pointing to references used. Expand
the docstring and explain viewing conditions. JCh hue is given in
degrees and needs to be converted to radians.
(lcms-d65-xyz): Remove. No need to duplicate this in Lisp or make the
API needlessly impure.
* test/src/lcms-tests.el: Reword commentary.
(lcms-rgb255->xyz): New function.
(lcms-cri-cam02-ucs): Fix let-binding.
(lcms-dE-cam02-ucs-silver): New test, assimilated from colorspacious.
Diffstat (limited to 'src')
| -rw-r--r-- | src/lcms.c | 107 |
1 files changed, 84 insertions, 23 deletions
diff --git a/src/lcms.c b/src/lcms.c index f543a030399..a5e527911ef 100644 --- a/src/lcms.c +++ b/src/lcms.c | |||
| @@ -139,6 +139,26 @@ chroma, and hue, respectively. The parameters each default to 1. */) | |||
| 139 | return make_float (cmsCIE2000DeltaE (&Lab1, &Lab2, Kl, Kc, Kh)); | 139 | return make_float (cmsCIE2000DeltaE (&Lab1, &Lab2, Kl, Kc, Kh)); |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | static double | ||
| 143 | deg2rad (double degrees) | ||
| 144 | { | ||
| 145 | return M_PI * degrees / 180.0; | ||
| 146 | } | ||
| 147 | |||
| 148 | static cmsCIEXYZ illuminant_d65 = { .X = 95.0455, .Y = 100.0, .Z = 108.8753 }; | ||
| 149 | |||
| 150 | static void | ||
| 151 | default_viewing_conditions (const cmsCIEXYZ *wp, cmsViewingConditions *vc) | ||
| 152 | { | ||
| 153 | vc->whitePoint.X = wp->X; | ||
| 154 | vc->whitePoint.Y = wp->Y; | ||
| 155 | vc->whitePoint.Z = wp->Z; | ||
| 156 | vc->Yb = 20; | ||
| 157 | vc->La = 100; | ||
| 158 | vc->surround = AVG_SURROUND; | ||
| 159 | vc->D_value = 1.0; | ||
| 160 | } | ||
| 161 | |||
| 142 | /* FIXME: code duplication */ | 162 | /* FIXME: code duplication */ |
| 143 | 163 | ||
| 144 | static bool | 164 | static bool |
| @@ -160,11 +180,62 @@ parse_xyz_list (Lisp_Object xyz_list, cmsCIEXYZ *color) | |||
| 160 | return true; | 180 | return true; |
| 161 | } | 181 | } |
| 162 | 182 | ||
| 163 | DEFUN ("lcms-cam02-ucs", Flcms_cam02_ucs, Slcms_cam02_ucs, 2, 3, 0, | 183 | static bool |
| 184 | parse_viewing_conditions (Lisp_Object view, const cmsCIEXYZ *wp, | ||
| 185 | cmsViewingConditions *vc) | ||
| 186 | { | ||
| 187 | #define PARSE_VIEW_CONDITION_FLOAT(field) \ | ||
| 188 | if (CONSP (view) && NUMBERP (XCAR (view))) \ | ||
| 189 | { \ | ||
| 190 | vc->field = XFLOATINT (XCAR (view)); \ | ||
| 191 | view = XCDR (view); \ | ||
| 192 | } \ | ||
| 193 | else \ | ||
| 194 | return false; | ||
| 195 | #define PARSE_VIEW_CONDITION_INT(field) \ | ||
| 196 | if (CONSP (view) && NATNUMP (XCAR (view))) \ | ||
| 197 | { \ | ||
| 198 | CHECK_RANGED_INTEGER (XCAR (view), 1, 4); \ | ||
| 199 | vc->field = XINT (XCAR (view)); \ | ||
| 200 | view = XCDR (view); \ | ||
| 201 | } \ | ||
| 202 | else \ | ||
| 203 | return false; | ||
| 204 | |||
| 205 | PARSE_VIEW_CONDITION_FLOAT (Yb); | ||
| 206 | PARSE_VIEW_CONDITION_FLOAT (La); | ||
| 207 | PARSE_VIEW_CONDITION_INT (surround); | ||
| 208 | PARSE_VIEW_CONDITION_FLOAT (D_value); | ||
| 209 | |||
| 210 | if (! NILP (view)) | ||
| 211 | return false; | ||
| 212 | |||
| 213 | vc->whitePoint.X = wp->X; | ||
| 214 | vc->whitePoint.Y = wp->Y; | ||
| 215 | vc->whitePoint.Z = wp->Z; | ||
| 216 | return true; | ||
| 217 | } | ||
| 218 | |||
| 219 | /* References: | ||
| 220 | Li, Luo et al. "The CRI-CAM02UCS colour rendering index." COLOR research | ||
| 221 | and application, 37 No.3, 2012. | ||
| 222 | Luo et al. "Uniform colour spaces based on CIECAM02 colour appearance | ||
| 223 | model." COLOR research and application, 31 No.4, 2006. */ | ||
| 224 | |||
| 225 | DEFUN ("lcms-cam02-ucs", Flcms_cam02_ucs, Slcms_cam02_ucs, 2, 4, 0, | ||
| 164 | doc: /* Compute CAM02-UCS metric distance between COLOR1 and COLOR2. | 226 | doc: /* Compute CAM02-UCS metric distance between COLOR1 and COLOR2. |
| 165 | Each color is a list of XYZ coordinates, with Y scaled about unity. | 227 | Each color is a list of XYZ tristimulus values, with Y scaled about unity. |
| 166 | Optional argument is the XYZ white point, which defaults to illuminant D65. */) | 228 | Optional argument WHITEPOINT is the XYZ white point, which defaults to |
| 167 | (Lisp_Object color1, Lisp_Object color2, Lisp_Object whitepoint) | 229 | illuminant D65. |
| 230 | Optional argument VIEW is a list containing the viewing conditions, and | ||
| 231 | is of the form (YB LA SURROUND DVALUE) where SURROUND corresponds to | ||
| 232 | 1 AVG_SURROUND | ||
| 233 | 2 DIM_SURROUND | ||
| 234 | 3 DARK_SURROUND | ||
| 235 | 4 CUTSHEET_SURROUND | ||
| 236 | The default viewing conditions are (20 100 1 1). */) | ||
| 237 | (Lisp_Object color1, Lisp_Object color2, Lisp_Object whitepoint, | ||
| 238 | Lisp_Object view) | ||
| 168 | { | 239 | { |
| 169 | cmsViewingConditions vc; | 240 | cmsViewingConditions vc; |
| 170 | cmsJCh jch1, jch2; | 241 | cmsJCh jch1, jch2; |
| @@ -188,17 +259,13 @@ Optional argument is the XYZ white point, which defaults to illuminant D65. */) | |||
| 188 | if (!(CONSP (color2) && parse_xyz_list (color2, &xyz2))) | 259 | if (!(CONSP (color2) && parse_xyz_list (color2, &xyz2))) |
| 189 | signal_error ("Invalid color", color2); | 260 | signal_error ("Invalid color", color2); |
| 190 | if (NILP (whitepoint)) | 261 | if (NILP (whitepoint)) |
| 191 | parse_xyz_list (Vlcms_d65_xyz, &xyzw); | 262 | xyzw = illuminant_d65; |
| 192 | else if (!(CONSP (whitepoint) && parse_xyz_list (whitepoint, &xyzw))) | 263 | else if (!(CONSP (whitepoint) && parse_xyz_list (whitepoint, &xyzw))) |
| 193 | signal_error ("Invalid white point", whitepoint); | 264 | signal_error ("Invalid white point", whitepoint); |
| 194 | 265 | if (NILP (view)) | |
| 195 | vc.whitePoint.X = xyzw.X; | 266 | default_viewing_conditions (&xyzw, &vc); |
| 196 | vc.whitePoint.Y = xyzw.Y; | 267 | else if (!(CONSP (view) && parse_viewing_conditions (view, &xyzw, &vc))) |
| 197 | vc.whitePoint.Z = xyzw.Z; | 268 | signal_error ("Invalid view conditions", view); |
| 198 | vc.Yb = 20; | ||
| 199 | vc.La = 100; | ||
| 200 | vc.surround = AVG_SURROUND; | ||
| 201 | vc.D_value = 1.0; | ||
| 202 | 269 | ||
| 203 | h1 = cmsCIECAM02Init (0, &vc); | 270 | h1 = cmsCIECAM02Init (0, &vc); |
| 204 | h2 = cmsCIECAM02Init (0, &vc); | 271 | h2 = cmsCIECAM02Init (0, &vc); |
| @@ -227,10 +294,10 @@ Optional argument is the XYZ white point, which defaults to illuminant D65. */) | |||
| 227 | Mp2 = 43.86 * log (1.0 + 0.0228 * (jch2.C * sqrt (sqrt (FL)))); | 294 | Mp2 = 43.86 * log (1.0 + 0.0228 * (jch2.C * sqrt (sqrt (FL)))); |
| 228 | Jp1 = 1.7 * jch1.J / (1.0 + (0.007 * jch1.J)); | 295 | Jp1 = 1.7 * jch1.J / (1.0 + (0.007 * jch1.J)); |
| 229 | Jp2 = 1.7 * jch2.J / (1.0 + (0.007 * jch2.J)); | 296 | Jp2 = 1.7 * jch2.J / (1.0 + (0.007 * jch2.J)); |
| 230 | ap1 = Mp1 * cos (jch1.h); | 297 | ap1 = Mp1 * cos (deg2rad (jch1.h)); |
| 231 | ap2 = Mp2 * cos (jch2.h); | 298 | ap2 = Mp2 * cos (deg2rad (jch2.h)); |
| 232 | bp1 = Mp1 * sin (jch1.h); | 299 | bp1 = Mp1 * sin (deg2rad (jch1.h)); |
| 233 | bp2 = Mp2 * sin (jch2.h); | 300 | bp2 = Mp2 * sin (deg2rad (jch2.h)); |
| 234 | 301 | ||
| 235 | return make_float (sqrt ((Jp2 - Jp1) * (Jp2 - Jp1) + | 302 | return make_float (sqrt ((Jp2 - Jp1) * (Jp2 - Jp1) + |
| 236 | (ap2 - ap1) * (ap2 - ap1) + | 303 | (ap2 - ap1) * (ap2 - ap1) + |
| @@ -291,12 +358,6 @@ DEFUN ("lcms2-available-p", Flcms2_available_p, Slcms2_available_p, 0, 0, 0, | |||
| 291 | void | 358 | void |
| 292 | syms_of_lcms2 (void) | 359 | syms_of_lcms2 (void) |
| 293 | { | 360 | { |
| 294 | DEFVAR_LISP ("lcms-d65-xyz", Vlcms_d65_xyz, | ||
| 295 | doc: /* D65 illuminant as a CIE XYZ triple. */); | ||
| 296 | Vlcms_d65_xyz = list3 (make_float (0.950455), | ||
| 297 | make_float (1.0), | ||
| 298 | make_float (1.088753)); | ||
| 299 | |||
| 300 | defsubr (&Slcms_cie_de2000); | 361 | defsubr (&Slcms_cie_de2000); |
| 301 | defsubr (&Slcms_cam02_ucs); | 362 | defsubr (&Slcms_cam02_ucs); |
| 302 | defsubr (&Slcms2_available_p); | 363 | defsubr (&Slcms2_available_p); |