aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMark Oteiza2017-09-21 22:47:24 -0400
committerMark Oteiza2017-09-21 23:06:00 -0400
commitd24ec5854098841388dfecf2c668e7f48f348af0 (patch)
treef8abf85b73a971d981874daa9876aa0640403ab3 /src
parenta81d5a3d3fcb76f6b074c2c721b80b1802135d41 (diff)
downloademacs-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.c107
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
142static double
143deg2rad (double degrees)
144{
145 return M_PI * degrees / 180.0;
146}
147
148static cmsCIEXYZ illuminant_d65 = { .X = 95.0455, .Y = 100.0, .Z = 108.8753 };
149
150static void
151default_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
144static bool 164static bool
@@ -160,11 +180,62 @@ parse_xyz_list (Lisp_Object xyz_list, cmsCIEXYZ *color)
160 return true; 180 return true;
161} 181}
162 182
163DEFUN ("lcms-cam02-ucs", Flcms_cam02_ucs, Slcms_cam02_ucs, 2, 3, 0, 183static bool
184parse_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
225DEFUN ("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.
165Each color is a list of XYZ coordinates, with Y scaled about unity. 227Each color is a list of XYZ tristimulus values, with Y scaled about unity.
166Optional argument is the XYZ white point, which defaults to illuminant D65. */) 228Optional argument WHITEPOINT is the XYZ white point, which defaults to
167 (Lisp_Object color1, Lisp_Object color2, Lisp_Object whitepoint) 229illuminant D65.
230Optional argument VIEW is a list containing the viewing conditions, and
231is 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
236The 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,
291void 358void
292syms_of_lcms2 (void) 359syms_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);