1 /* Interface to Little CMS
2 Copyright (C) 2017 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or (at
9 your option) any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
39 DEF_DLL_FN (cmsFloat64Number
, cmsCIE2000DeltaE
,
40 (const cmsCIELab
* Lab1
, const cmsCIELab
* Lab2
, cmsFloat64Number Kl
,
41 cmsFloat64Number Kc
, cmsFloat64Number Kh
));
42 DEF_DLL_FN (cmsHANDLE
, cmsCIECAM02Init
,
43 (cmsContext ContextID
, const cmsViewingConditions
* pVC
));
44 DEF_DLL_FN (void, cmsCIECAM02Forward
,
45 (cmsHANDLE hModel
, const cmsCIEXYZ
* pIn
, cmsJCh
* pOut
));
46 DEF_DLL_FN (void, cmsCIECAM02Reverse
,
47 (cmsHANDLE hModel
, const cmsJCh
* pIn
, cmsCIEXYZ
* pOut
));
48 DEF_DLL_FN (void, cmsCIECAM02Done
, (cmsHANDLE hModel
));
49 DEF_DLL_FN (cmsBool
, cmsWhitePointFromTemp
,
50 (cmsCIExyY
* WhitePoint
, cmsFloat64Number TempK
));
51 DEF_DLL_FN (void, cmsxyY2XYZ
, (cmsCIEXYZ
* Dest
, const cmsCIExyY
* Source
));
53 static bool lcms_initialized
;
56 init_lcms_functions (void)
58 HMODULE library
= w32_delayed_load (Qlcms2
);
63 LOAD_DLL_FN (library
, cmsCIE2000DeltaE
);
64 LOAD_DLL_FN (library
, cmsCIECAM02Init
);
65 LOAD_DLL_FN (library
, cmsCIECAM02Forward
);
66 LOAD_DLL_FN (library
, cmsCIECAM02Reverse
);
67 LOAD_DLL_FN (library
, cmsCIECAM02Done
);
68 LOAD_DLL_FN (library
, cmsWhitePointFromTemp
);
69 LOAD_DLL_FN (library
, cmsxyY2XYZ
);
73 # undef cmsCIE2000DeltaE
74 # undef cmsCIECAM02Init
75 # undef cmsCIECAM02Forward
76 # undef cmsCIECAM02Reverse
77 # undef cmsCIECAM02Done
78 # undef cmsWhitePointFromTemp
81 # define cmsCIE2000DeltaE fn_cmsCIE2000DeltaE
82 # define cmsCIECAM02Init fn_cmsCIECAM02Init
83 # define cmsCIECAM02Forward fn_cmsCIECAM02Forward
84 # define cmsCIECAM02Reverse fn_cmsCIECAM02Reverse
85 # define cmsCIECAM02Done fn_cmsCIECAM02Done
86 # define cmsWhitePointFromTemp fn_cmsWhitePointFromTemp
87 # define cmsxyY2XYZ fn_cmsxyY2XYZ
89 #endif /* WINDOWSNT */
92 parse_lab_list (Lisp_Object lab_list
, cmsCIELab
*color
)
94 #define PARSE_LAB_LIST_FIELD(field) \
95 if (CONSP (lab_list) && NUMBERP (XCAR (lab_list))) \
97 color->field = XFLOATINT (XCAR (lab_list)); \
98 lab_list = XCDR (lab_list); \
103 PARSE_LAB_LIST_FIELD (L
);
104 PARSE_LAB_LIST_FIELD (a
);
105 PARSE_LAB_LIST_FIELD (b
);
110 /* http://www.ece.rochester.edu/~gsharma/ciede2000/ciede2000noteCRNA.pdf> */
112 DEFUN ("lcms-cie-de2000", Flcms_cie_de2000
, Slcms_cie_de2000
, 2, 5, 0,
113 doc
: /* Compute CIEDE2000 metric distance between COLOR1 and COLOR2.
114 Each color is a list of L*a*b* coordinates, where the L* channel ranges from
115 0 to 100, and the a* and b* channels range from -128 to 128.
116 Optional arguments KL, KC, KH are weighting parameters for lightness,
117 chroma, and hue, respectively. The parameters each default to 1. */)
118 (Lisp_Object color1
, Lisp_Object color2
,
119 Lisp_Object kL
, Lisp_Object kC
, Lisp_Object kH
)
121 cmsCIELab Lab1
, Lab2
;
122 cmsFloat64Number Kl
, Kc
, Kh
;
125 if (!lcms_initialized
)
126 lcms_initialized
= init_lcms_functions ();
127 if (!lcms_initialized
)
129 message1 ("lcms2 library not found");
134 if (!(CONSP (color1
) && parse_lab_list (color1
, &Lab1
)))
135 signal_error ("Invalid color", color1
);
136 if (!(CONSP (color2
) && parse_lab_list (color2
, &Lab2
)))
137 signal_error ("Invalid color", color1
);
140 else if (!(NUMBERP (kL
) && (Kl
= XFLOATINT(kL
))))
141 wrong_type_argument(Qnumberp
, kL
);
144 else if (!(NUMBERP (kC
) && (Kc
= XFLOATINT(kC
))))
145 wrong_type_argument(Qnumberp
, kC
);
148 else if (!(NUMBERP (kH
) && (Kh
= XFLOATINT(kH
))))
149 wrong_type_argument(Qnumberp
, kH
);
151 return make_float (cmsCIE2000DeltaE (&Lab1
, &Lab2
, Kl
, Kc
, Kh
));
155 deg2rad (double degrees
)
157 return M_PI
* degrees
/ 180.0;
161 rad2deg (double radians
)
163 return 180.0 * radians
/ M_PI
;
166 static cmsCIEXYZ illuminant_d65
= { .X
= 95.0455, .Y
= 100.0, .Z
= 108.8753 };
169 default_viewing_conditions (const cmsCIEXYZ
*wp
, cmsViewingConditions
*vc
)
171 vc
->whitePoint
.X
= wp
->X
;
172 vc
->whitePoint
.Y
= wp
->Y
;
173 vc
->whitePoint
.Z
= wp
->Z
;
176 vc
->surround
= AVG_SURROUND
;
180 /* FIXME: code duplication */
183 parse_xyz_list (Lisp_Object xyz_list
, cmsCIEXYZ
*color
)
185 #define PARSE_XYZ_LIST_FIELD(field) \
186 if (CONSP (xyz_list) && NUMBERP (XCAR (xyz_list))) \
188 color->field = 100.0 * XFLOATINT (XCAR (xyz_list)); \
189 xyz_list = XCDR (xyz_list); \
194 PARSE_XYZ_LIST_FIELD (X
);
195 PARSE_XYZ_LIST_FIELD (Y
);
196 PARSE_XYZ_LIST_FIELD (Z
);
202 parse_jch_list (Lisp_Object jch_list
, cmsJCh
*color
)
204 #define PARSE_JCH_LIST_FIELD(field) \
205 if (CONSP (jch_list) && NUMBERP (XCAR (jch_list))) \
207 color->field = XFLOATINT (XCAR (jch_list)); \
208 jch_list = XCDR (jch_list); \
213 PARSE_JCH_LIST_FIELD (J
);
214 PARSE_JCH_LIST_FIELD (C
);
215 PARSE_JCH_LIST_FIELD (h
);
217 if (! NILP (jch_list
))
223 parse_jab_list (Lisp_Object jab_list
, lcmsJab_t
*color
)
225 #define PARSE_JAB_LIST_FIELD(field) \
226 if (CONSP (jab_list) && NUMBERP (XCAR (jab_list))) \
228 color->field = XFLOATINT (XCAR (jab_list)); \
229 jab_list = XCDR (jab_list); \
234 PARSE_JAB_LIST_FIELD (J
);
235 PARSE_JAB_LIST_FIELD (a
);
236 PARSE_JAB_LIST_FIELD (b
);
242 parse_viewing_conditions (Lisp_Object view
, const cmsCIEXYZ
*wp
,
243 cmsViewingConditions
*vc
)
245 #define PARSE_VIEW_CONDITION_FLOAT(field) \
246 if (CONSP (view) && NUMBERP (XCAR (view))) \
248 vc->field = XFLOATINT (XCAR (view)); \
249 view = XCDR (view); \
253 #define PARSE_VIEW_CONDITION_INT(field) \
254 if (CONSP (view) && NATNUMP (XCAR (view))) \
256 CHECK_RANGED_INTEGER (XCAR (view), 1, 4); \
257 vc->field = XINT (XCAR (view)); \
258 view = XCDR (view); \
263 PARSE_VIEW_CONDITION_FLOAT (Yb
);
264 PARSE_VIEW_CONDITION_FLOAT (La
);
265 PARSE_VIEW_CONDITION_INT (surround
);
266 PARSE_VIEW_CONDITION_FLOAT (D_value
);
271 vc
->whitePoint
.X
= wp
->X
;
272 vc
->whitePoint
.Y
= wp
->Y
;
273 vc
->whitePoint
.Z
= wp
->Z
;
278 xyz_to_jch (const cmsCIEXYZ
*xyz
, cmsJCh
*jch
, const cmsViewingConditions
*vc
)
282 h
= cmsCIECAM02Init (0, vc
);
283 cmsCIECAM02Forward (h
, xyz
, jch
);
288 jch_to_xyz (const cmsJCh
*jch
, cmsCIEXYZ
*xyz
, const cmsViewingConditions
*vc
)
292 h
= cmsCIECAM02Init (0, vc
);
293 cmsCIECAM02Reverse (h
, jch
, xyz
);
298 jch_to_jab (const cmsJCh
*jch
, lcmsJab_t
*jab
, double FL
, double c1
, double c2
)
300 double Mp
= 43.86 * log (1.0 + c2
* (jch
->C
* sqrt (sqrt (FL
))));
301 jab
->J
= 1.7 * jch
->J
/ (1.0 + (c1
* jch
->J
));
302 jab
->a
= Mp
* cos (deg2rad (jch
->h
));
303 jab
->b
= Mp
* sin (deg2rad (jch
->h
));
307 jab_to_jch (const lcmsJab_t
*jab
, cmsJCh
*jch
, double FL
, double c1
, double c2
)
309 jch
->J
= jab
->J
/ (1.0 + c1
* (100.0 - jab
->J
));
310 jch
->h
= atan2 (jab
->b
, jab
->a
);
311 double Mp
= hypot (jab
->a
, jab
->b
);
312 jch
->h
= rad2deg (jch
->h
);
315 jch
->C
= (exp (c2
* Mp
) - 1.0) / (c2
* sqrt (sqrt (FL
)));
318 DEFUN ("lcms-xyz->jch", Flcms_xyz_to_jch
, Slcms_xyz_to_jch
, 1, 3, 0,
319 doc
: /* Convert CIE CAM02 JCh to CIE XYZ.
320 COLOR is a list (X Y Z), with Y scaled about unity.
321 Optional arguments WHITEPOINT and VIEW are the same as in `lcms-cam02-ucs',
323 (Lisp_Object color
, Lisp_Object whitepoint
, Lisp_Object view
)
325 cmsViewingConditions vc
;
330 if (!lcms_initialized
)
331 lcms_initialized
= init_lcms_functions ();
332 if (!lcms_initialized
)
334 message1 ("lcms2 library not found");
339 if (!(CONSP (color
) && parse_xyz_list (color
, &xyz
)))
340 signal_error ("Invalid color", color
);
341 if (NILP (whitepoint
))
342 xyzw
= illuminant_d65
;
343 else if (!(CONSP (whitepoint
) && parse_xyz_list (whitepoint
, &xyzw
)))
344 signal_error ("Invalid white point", whitepoint
);
346 default_viewing_conditions (&xyzw
, &vc
);
347 else if (!(CONSP (view
) && parse_viewing_conditions (view
, &xyzw
, &vc
)))
348 signal_error ("Invalid viewing conditions", view
);
350 xyz_to_jch(&xyz
, &jch
, &vc
);
351 return list3 (make_float (jch
.J
), make_float (jch
.C
), make_float (jch
.h
));
354 DEFUN ("lcms-jch->xyz", Flcms_jch_to_xyz
, Slcms_jch_to_xyz
, 1, 3, 0,
355 doc
: /* Convert CIE XYZ to CIE CAM02 JCh.
356 COLOR is a list (J C h), where lightness of white is equal to 100, and hue
358 Optional arguments WHITEPOINT and VIEW are the same as in `lcms-cam02-ucs',
360 (Lisp_Object color
, Lisp_Object whitepoint
, Lisp_Object view
)
362 cmsViewingConditions vc
;
367 if (!lcms_initialized
)
368 lcms_initialized
= init_lcms_functions ();
369 if (!lcms_initialized
)
371 message1 ("lcms2 library not found");
376 if (!(CONSP (color
) && parse_jch_list (color
, &jch
)))
377 signal_error ("Invalid color", color
);
378 if (NILP (whitepoint
))
379 xyzw
= illuminant_d65
;
380 else if (!(CONSP (whitepoint
) && parse_xyz_list (whitepoint
, &xyzw
)))
381 signal_error ("Invalid white point", whitepoint
);
383 default_viewing_conditions (&xyzw
, &vc
);
384 else if (!(CONSP (view
) && parse_viewing_conditions (view
, &xyzw
, &vc
)))
385 signal_error ("Invalid viewing conditions", view
);
387 jch_to_xyz(&jch
, &xyz
, &vc
);
388 return list3 (make_float (xyz
.X
/ 100.0),
389 make_float (xyz
.Y
/ 100.0),
390 make_float (xyz
.Z
/ 100.0));
393 DEFUN ("lcms-jch->jab", Flcms_jch_to_jab
, Slcms_jch_to_jab
, 1, 3, 0,
394 doc
: /* Convert CIE CAM02 JCh to CAM02-UCS J'a'b'.
395 COLOR is a list (J C h) as described in `lcms-jch->xyz', which see.
396 Optional arguments WHITEPOINT and VIEW are the same as in `lcms-cam02-ucs',
398 (Lisp_Object color
, Lisp_Object whitepoint
, Lisp_Object view
)
400 cmsViewingConditions vc
;
407 if (!lcms_initialized
)
408 lcms_initialized
= init_lcms_functions ();
409 if (!lcms_initialized
)
411 message1 ("lcms2 library not found");
416 if (!(CONSP (color
) && parse_jch_list (color
, &jch
)))
417 signal_error ("Invalid color", color
);
418 if (NILP (whitepoint
))
419 xyzw
= illuminant_d65
;
420 else if (!(CONSP (whitepoint
) && parse_xyz_list (whitepoint
, &xyzw
)))
421 signal_error ("Invalid white point", whitepoint
);
423 default_viewing_conditions (&xyzw
, &vc
);
424 else if (!(CONSP (view
) && parse_viewing_conditions (view
, &xyzw
, &vc
)))
425 signal_error ("Invalid viewing conditions", view
);
427 k
= 1.0 / (1.0 + (5.0 * vc
.La
));
429 FL
= vc
.La
* k4
+ 0.1 * (1 - k4
) * (1 - k4
) * cbrt (5.0 * vc
.La
);
430 jch_to_jab (&jch
, &jab
, FL
, 0.007, 0.0228);
431 return list3 (make_float (jab
.J
), make_float (jab
.a
), make_float (jab
.b
));
434 DEFUN ("lcms-jab->jch", Flcms_jab_to_jch
, Slcms_jab_to_jch
, 1, 3, 0,
435 doc
: /* Convert CAM02-UCS J'a'b' to CIE CAM02 JCh.
436 COLOR is a list (J' a' b'), where white corresponds to lightness J equal to 100.
437 Optional arguments WHITEPOINT and VIEW are the same as in `lcms-cam02-ucs',
439 (Lisp_Object color
, Lisp_Object whitepoint
, Lisp_Object view
)
441 cmsViewingConditions vc
;
448 if (!lcms_initialized
)
449 lcms_initialized
= init_lcms_functions ();
450 if (!lcms_initialized
)
452 message1 ("lcms2 library not found");
457 if (!(CONSP (color
) && parse_jab_list (color
, &jab
)))
458 signal_error ("Invalid color", color
);
459 if (NILP (whitepoint
))
460 xyzw
= illuminant_d65
;
461 else if (!(CONSP (whitepoint
) && parse_xyz_list (whitepoint
, &xyzw
)))
462 signal_error ("Invalid white point", whitepoint
);
464 default_viewing_conditions (&xyzw
, &vc
);
465 else if (!(CONSP (view
) && parse_viewing_conditions (view
, &xyzw
, &vc
)))
466 signal_error ("Invalid viewing conditions", view
);
468 k
= 1.0 / (1.0 + (5.0 * vc
.La
));
470 FL
= vc
.La
* k4
+ 0.1 * (1 - k4
) * (1 - k4
) * cbrt (5.0 * vc
.La
);
471 jab_to_jch (&jab
, &jch
, FL
, 0.007, 0.0228);
472 return list3 (make_float (jch
.J
), make_float (jch
.C
), make_float (jch
.h
));
476 Li, Luo et al. "The CRI-CAM02UCS colour rendering index." COLOR research
477 and application, 37 No.3, 2012.
478 Luo et al. "Uniform colour spaces based on CIECAM02 colour appearance
479 model." COLOR research and application, 31 No.4, 2006. */
481 DEFUN ("lcms-cam02-ucs", Flcms_cam02_ucs
, Slcms_cam02_ucs
, 2, 4, 0,
482 doc
: /* Compute CAM02-UCS metric distance between COLOR1 and COLOR2.
483 Each color is a list of XYZ tristimulus values, with Y scaled about unity.
484 Optional argument WHITEPOINT is the XYZ white point, which defaults to
486 Optional argument VIEW is a list containing the viewing conditions, and
487 is of the form (YB LA SURROUND DVALUE) where SURROUND corresponds to
492 The default viewing conditions are (20 100 1 1). */)
493 (Lisp_Object color1
, Lisp_Object color2
, Lisp_Object whitepoint
,
496 cmsViewingConditions vc
;
498 cmsCIEXYZ xyz1
, xyz2
, xyzw
;
499 lcmsJab_t jab1
, jab2
;
503 if (!lcms_initialized
)
504 lcms_initialized
= init_lcms_functions ();
505 if (!lcms_initialized
)
507 message1 ("lcms2 library not found");
512 if (!(CONSP (color1
) && parse_xyz_list (color1
, &xyz1
)))
513 signal_error ("Invalid color", color1
);
514 if (!(CONSP (color2
) && parse_xyz_list (color2
, &xyz2
)))
515 signal_error ("Invalid color", color2
);
516 if (NILP (whitepoint
))
517 xyzw
= illuminant_d65
;
518 else if (!(CONSP (whitepoint
) && parse_xyz_list (whitepoint
, &xyzw
)))
519 signal_error ("Invalid white point", whitepoint
);
521 default_viewing_conditions (&xyzw
, &vc
);
522 else if (!(CONSP (view
) && parse_viewing_conditions (view
, &xyzw
, &vc
)))
523 signal_error ("Invalid view conditions", view
);
525 xyz_to_jch (&xyz1
, &jch1
, &vc
);
526 xyz_to_jch (&xyz2
, &jch2
, &vc
);
528 k
= 1.0 / (1.0 + (5.0 * vc
.La
));
530 FL
= vc
.La
* k4
+ 0.1 * (1 - k4
) * (1 - k4
) * cbrt (5.0 * vc
.La
);
531 jch_to_jab (&jch1
, &jab1
, FL
, 0.007, 0.0228);
532 jch_to_jab (&jch2
, &jab2
, FL
, 0.007, 0.0228);
534 return make_float (hypot (jab2
.J
- jab1
.J
,
535 hypot (jab2
.a
- jab1
.a
, jab2
.b
- jab1
.b
)));
538 DEFUN ("lcms-temp->white-point", Flcms_temp_to_white_point
, Slcms_temp_to_white_point
, 1, 1, 0,
539 doc
: /* Return XYZ black body chromaticity from TEMPERATURE given in K.
540 Valid range of TEMPERATURE is from 4000K to 25000K. */)
541 (Lisp_Object temperature
)
543 cmsFloat64Number tempK
;
544 cmsCIExyY whitepoint
;
548 if (!lcms_initialized
)
549 lcms_initialized
= init_lcms_functions ();
550 if (!lcms_initialized
)
552 message1 ("lcms2 library not found");
557 CHECK_NUMBER_OR_FLOAT (temperature
);
559 tempK
= XFLOATINT (temperature
);
560 if (!(cmsWhitePointFromTemp (&whitepoint
, tempK
)))
561 signal_error("Invalid temperature", temperature
);
562 cmsxyY2XYZ (&wp
, &whitepoint
);
563 return list3 (make_float (wp
.X
), make_float (wp
.Y
), make_float (wp
.Z
));
566 DEFUN ("lcms2-available-p", Flcms2_available_p
, Slcms2_available_p
, 0, 0, 0,
567 doc
: /* Return t if lcms2 color calculations are available in this instance of Emacs. */)
571 Lisp_Object found
= Fassq (Qlcms2
, Vlibrary_cache
);
577 lcms_initialized
= init_lcms_functions ();
578 status
= lcms_initialized
? Qt
: Qnil
;
579 Vlibrary_cache
= Fcons (Fcons (Qlcms2
, status
), Vlibrary_cache
);
582 #else /* !WINDOWSNT */
592 defsubr (&Slcms_cie_de2000
);
593 defsubr (&Slcms_xyz_to_jch
);
594 defsubr (&Slcms_jch_to_xyz
);
595 defsubr (&Slcms_jch_to_jab
);
596 defsubr (&Slcms_jab_to_jch
);
597 defsubr (&Slcms_cam02_ucs
);
598 defsubr (&Slcms2_available_p
);
599 defsubr (&Slcms_temp_to_white_point
);
601 Fprovide (intern_c_string ("lcms2"), Qnil
);
604 #endif /* HAVE_LCMS2 */