diff options
| author | Mark Oteiza | 2017-09-13 10:27:37 -0400 |
|---|---|---|
| committer | Mark Oteiza | 2017-09-13 10:27:37 -0400 |
| commit | 5d4c539bd7e15e7fd0fb092276791b6287260a9a (patch) | |
| tree | cc46c19ff16d9f12a6f6f8c2e2f5ba5807654c31 /src | |
| parent | 9a8bbb9d5d3a55d4a31658e188f305669bd26e79 (diff) | |
| download | emacs-5d4c539bd7e15e7fd0fb092276791b6287260a9a.tar.gz emacs-5d4c539bd7e15e7fd0fb092276791b6287260a9a.zip | |
Add lcms2 interface
configure.ac: Add boilerplate for configuring and detecting liblcms2.
etc/NEWS: Mention new configure option and color-distance change.
src/Makefile.in: Add references to lcms.c and liblcms.
src/emacs.c: Define lcms2 symbols.
src/lcms.c: New file.
src/lisp.h: Add declaration for lcms2.
src/xfaces.c: Add optional METRIC argument.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.in | 6 | ||||
| -rw-r--r-- | src/emacs.c | 4 | ||||
| -rw-r--r-- | src/lcms.c | 182 | ||||
| -rw-r--r-- | src/lisp.h | 5 | ||||
| -rw-r--r-- | src/xfaces.c | 15 |
5 files changed, 205 insertions, 7 deletions
diff --git a/src/Makefile.in b/src/Makefile.in index dde3f1d3fb4..a98ad9c5ebd 100644 --- a/src/Makefile.in +++ b/src/Makefile.in | |||
| @@ -234,6 +234,8 @@ LIBXML2_CFLAGS = @LIBXML2_CFLAGS@ | |||
| 234 | 234 | ||
| 235 | GETADDRINFO_A_LIBS = @GETADDRINFO_A_LIBS@ | 235 | GETADDRINFO_A_LIBS = @GETADDRINFO_A_LIBS@ |
| 236 | 236 | ||
| 237 | LIBLCMS2 = @LIBLCMS2@ | ||
| 238 | |||
| 237 | LIBZ = @LIBZ@ | 239 | LIBZ = @LIBZ@ |
| 238 | 240 | ||
| 239 | ## system-specific libs for dynamic modules, else empty | 241 | ## system-specific libs for dynamic modules, else empty |
| @@ -389,7 +391,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \ | |||
| 389 | syntax.o $(UNEXEC_OBJ) bytecode.o \ | 391 | syntax.o $(UNEXEC_OBJ) bytecode.o \ |
| 390 | process.o gnutls.o callproc.o \ | 392 | process.o gnutls.o callproc.o \ |
| 391 | region-cache.o sound.o atimer.o \ | 393 | region-cache.o sound.o atimer.o \ |
| 392 | doprnt.o intervals.o textprop.o composite.o xml.o $(NOTIFY_OBJ) \ | 394 | doprnt.o intervals.o textprop.o composite.o xml.o lcms.o $(NOTIFY_OBJ) \ |
| 393 | $(XWIDGETS_OBJ) \ | 395 | $(XWIDGETS_OBJ) \ |
| 394 | profiler.o decompress.o \ | 396 | profiler.o decompress.o \ |
| 395 | thread.o systhread.o \ | 397 | thread.o systhread.o \ |
| @@ -490,7 +492,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \ | |||
| 490 | $(LIBXML2_LIBS) $(LIBGPM) $(LIBS_SYSTEM) $(CAIRO_LIBS) \ | 492 | $(LIBXML2_LIBS) $(LIBGPM) $(LIBS_SYSTEM) $(CAIRO_LIBS) \ |
| 491 | $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ | 493 | $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ |
| 492 | $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ | 494 | $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ |
| 493 | $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) \ | 495 | $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LIBLCMS2) \ |
| 494 | $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) | 496 | $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) |
| 495 | 497 | ||
| 496 | ## FORCE it so that admin/unidata can decide whether these files | 498 | ## FORCE it so that admin/unidata can decide whether these files |
diff --git a/src/emacs.c b/src/emacs.c index 44f6285795a..668711a5ab9 100644 --- a/src/emacs.c +++ b/src/emacs.c | |||
| @@ -1546,6 +1546,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem | |||
| 1546 | syms_of_xml (); | 1546 | syms_of_xml (); |
| 1547 | #endif | 1547 | #endif |
| 1548 | 1548 | ||
| 1549 | #ifdef HAVE_LCMS2 | ||
| 1550 | syms_of_lcms2 (); | ||
| 1551 | #endif | ||
| 1552 | |||
| 1549 | #ifdef HAVE_ZLIB | 1553 | #ifdef HAVE_ZLIB |
| 1550 | syms_of_decompress (); | 1554 | syms_of_decompress (); |
| 1551 | #endif | 1555 | #endif |
diff --git a/src/lcms.c b/src/lcms.c new file mode 100644 index 00000000000..120ef769810 --- /dev/null +++ b/src/lcms.c | |||
| @@ -0,0 +1,182 @@ | |||
| 1 | /* Interface to Little CMS | ||
| 2 | Copyright (C) 2017 Free Software Foundation, Inc. | ||
| 3 | |||
| 4 | This file is part of GNU Emacs. | ||
| 5 | |||
| 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. | ||
| 10 | |||
| 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. | ||
| 15 | |||
| 16 | You should have received a copy of the GNU General Public License | ||
| 17 | along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | ||
| 18 | |||
| 19 | #include <config.h> | ||
| 20 | |||
| 21 | #ifdef HAVE_LCMS2 | ||
| 22 | |||
| 23 | #include <lcms2.h> | ||
| 24 | #include <math.h> | ||
| 25 | |||
| 26 | #include "lisp.h" | ||
| 27 | |||
| 28 | static bool | ||
| 29 | parse_lab_list (Lisp_Object lab_list, cmsCIELab *color) | ||
| 30 | { | ||
| 31 | #define PARSE_LAB_LIST_FIELD(field) \ | ||
| 32 | if (CONSP (lab_list) && NUMBERP (XCAR (lab_list))) \ | ||
| 33 | { \ | ||
| 34 | color->field = XFLOATINT (XCAR (lab_list)); \ | ||
| 35 | lab_list = XCDR (lab_list); \ | ||
| 36 | } \ | ||
| 37 | else \ | ||
| 38 | return false; | ||
| 39 | |||
| 40 | PARSE_LAB_LIST_FIELD (L); | ||
| 41 | PARSE_LAB_LIST_FIELD (a); | ||
| 42 | PARSE_LAB_LIST_FIELD (b); | ||
| 43 | |||
| 44 | return true; | ||
| 45 | } | ||
| 46 | |||
| 47 | /* http://www.ece.rochester.edu/~gsharma/ciede2000/ciede2000noteCRNA.pdf> */ | ||
| 48 | |||
| 49 | DEFUN ("lcms-cie-de2000", Flcms_cie_de2000, Slcms_cie_de2000, 2, 5, 0, | ||
| 50 | doc: /* Compute CIEDE2000 metric distance between COLOR1 and COLOR2. | ||
| 51 | Each color is a list of L*a*b* coordinates, where the L* channel ranges from | ||
| 52 | 0 to 100, and the a* and b* channels range from -128 to 128. | ||
| 53 | Optional arguments KL, KC, KH are weighting parameters for lightness, | ||
| 54 | chroma, and hue, respectively. The parameters each default to 1. */) | ||
| 55 | (Lisp_Object color1, Lisp_Object color2, | ||
| 56 | Lisp_Object kL, Lisp_Object kC, Lisp_Object kH) | ||
| 57 | { | ||
| 58 | cmsCIELab Lab1, Lab2; | ||
| 59 | cmsFloat64Number Kl, Kc, Kh; | ||
| 60 | |||
| 61 | if (!(CONSP (color1) && parse_lab_list (color1, &Lab1))) | ||
| 62 | signal_error ("Invalid color", color1); | ||
| 63 | if (!(CONSP (color2) && parse_lab_list (color2, &Lab2))) | ||
| 64 | signal_error ("Invalid color", color1); | ||
| 65 | if (NILP (kL)) | ||
| 66 | Kl = 1.0f; | ||
| 67 | else if (!(NUMBERP (kL) && (Kl = XFLOATINT(kL)))) | ||
| 68 | wrong_type_argument(Qnumberp, kL); | ||
| 69 | if (NILP (kC)) | ||
| 70 | Kc = 1.0f; | ||
| 71 | else if (!(NUMBERP (kC) && (Kc = XFLOATINT(kC)))) | ||
| 72 | wrong_type_argument(Qnumberp, kC); | ||
| 73 | if (NILP (kL)) | ||
| 74 | Kh = 1.0f; | ||
| 75 | else if (!(NUMBERP (kH) && (Kh = XFLOATINT(kH)))) | ||
| 76 | wrong_type_argument(Qnumberp, kH); | ||
| 77 | |||
| 78 | return make_float (cmsCIE2000DeltaE (&Lab1, &Lab2, Kl, Kc, Kh)); | ||
| 79 | } | ||
| 80 | |||
| 81 | /* FIXME: code duplication */ | ||
| 82 | |||
| 83 | static bool | ||
| 84 | parse_xyz_list (Lisp_Object xyz_list, cmsCIEXYZ *color) | ||
| 85 | { | ||
| 86 | #define PARSE_XYZ_LIST_FIELD(field) \ | ||
| 87 | if (CONSP (xyz_list) && NUMBERP (XCAR (xyz_list))) \ | ||
| 88 | { \ | ||
| 89 | color->field = 100.0 * XFLOATINT (XCAR (xyz_list)); \ | ||
| 90 | xyz_list = XCDR (xyz_list); \ | ||
| 91 | } \ | ||
| 92 | else \ | ||
| 93 | return false; | ||
| 94 | |||
| 95 | PARSE_XYZ_LIST_FIELD (X); | ||
| 96 | PARSE_XYZ_LIST_FIELD (Y); | ||
| 97 | PARSE_XYZ_LIST_FIELD (Z); | ||
| 98 | |||
| 99 | return true; | ||
| 100 | } | ||
| 101 | |||
| 102 | DEFUN ("lcms-cam02-ucs", Flcms_cam02_ucs, Slcms_cam02_ucs, 2, 3, 0, | ||
| 103 | doc: /* Compute CAM02-UCS metric distance between COLOR1 and COLOR2. | ||
| 104 | Each color is a list of XYZ coordinates, with Y scaled to unity. | ||
| 105 | Optional argument is the XYZ white point, which defaults to illuminant D65. */) | ||
| 106 | (Lisp_Object color1, Lisp_Object color2, Lisp_Object whitepoint) | ||
| 107 | { | ||
| 108 | cmsViewingConditions vc; | ||
| 109 | cmsJCh jch1, jch2; | ||
| 110 | cmsHANDLE h1, h2; | ||
| 111 | cmsCIEXYZ xyz1, xyz2, xyzw; | ||
| 112 | double Jp1, ap1, bp1, Jp2, ap2, bp2; | ||
| 113 | double Mp1, Mp2, FL, k, k4; | ||
| 114 | |||
| 115 | if (!(CONSP (color1) && parse_xyz_list (color1, &xyz1))) | ||
| 116 | signal_error ("Invalid color", color1); | ||
| 117 | if (!(CONSP (color2) && parse_xyz_list (color2, &xyz2))) | ||
| 118 | signal_error ("Invalid color", color1); | ||
| 119 | if (NILP (whitepoint)) | ||
| 120 | { | ||
| 121 | xyzw.X = 95.047; | ||
| 122 | xyzw.Y = 100.0; | ||
| 123 | xyzw.Z = 108.883; | ||
| 124 | } | ||
| 125 | else if (!(CONSP (whitepoint) && parse_xyz_list(whitepoint, &xyzw))) | ||
| 126 | signal_error("Invalid white point", whitepoint); | ||
| 127 | |||
| 128 | vc.whitePoint.X = xyzw.X; | ||
| 129 | vc.whitePoint.Y = xyzw.Y; | ||
| 130 | vc.whitePoint.Z = xyzw.Z; | ||
| 131 | vc.Yb = 20; | ||
| 132 | vc.La = 100; | ||
| 133 | vc.surround = AVG_SURROUND; | ||
| 134 | vc.D_value = 1.0; | ||
| 135 | |||
| 136 | h1 = cmsCIECAM02Init (0, &vc); | ||
| 137 | h2 = cmsCIECAM02Init (0, &vc); | ||
| 138 | cmsCIECAM02Forward (h1, &xyz1, &jch1); | ||
| 139 | cmsCIECAM02Forward (h2, &xyz2, &jch2); | ||
| 140 | cmsCIECAM02Done (h1); | ||
| 141 | cmsCIECAM02Done (h2); | ||
| 142 | |||
| 143 | /* Now have colors in JCh, need to calculate J'a'b' | ||
| 144 | |||
| 145 | M = C * F_L^0.25 | ||
| 146 | J' = 1.7 J / (1 + 0.007 J) | ||
| 147 | M' = 43.86 ln(1 + 0.0228 M) | ||
| 148 | a' = M' cos(h) | ||
| 149 | b' = M' sin(h) | ||
| 150 | |||
| 151 | where | ||
| 152 | |||
| 153 | F_L = 0.2 k^4 (5 L_A) + 0.1 (1 - k^4)^2 (5 L_A)^(1/3), | ||
| 154 | k = 1/(5 L_A + 1) | ||
| 155 | */ | ||
| 156 | k = 1.0 / (1.0 + (5.0 * vc.La)); | ||
| 157 | k4 = k * k * k * k; | ||
| 158 | FL = vc.La * k4 + 0.1 * (1 - k4) * (1 - k4) * cbrt (5.0 * vc.La); | ||
| 159 | Mp1 = 43.86 * log (1.0 + 0.0228 * (jch1.C * sqrt (sqrt (FL)))); | ||
| 160 | Mp2 = 43.86 * log (1.0 + 0.0228 * (jch2.C * sqrt (sqrt (FL)))); | ||
| 161 | Jp1 = 1.7 * jch1.J / (1.0 + (0.007 * jch1.J)); | ||
| 162 | Jp2 = 1.7 * jch2.J / (1.0 + (0.007 * jch2.J)); | ||
| 163 | ap1 = Mp1 * cos (jch1.h); | ||
| 164 | ap2 = Mp2 * cos (jch2.h); | ||
| 165 | bp1 = Mp1 * sin (jch1.h); | ||
| 166 | bp2 = Mp2 * sin (jch2.h); | ||
| 167 | |||
| 168 | return make_float (sqrt ((Jp2 - Jp1) * (Jp2 - Jp1) + | ||
| 169 | (ap2 - ap1) * (ap2 - ap1) + | ||
| 170 | (bp2 - bp1) * (bp2 - bp1))); | ||
| 171 | } | ||
| 172 | |||
| 173 | |||
| 174 | /* Initialization */ | ||
| 175 | void | ||
| 176 | syms_of_lcms2 (void) | ||
| 177 | { | ||
| 178 | defsubr (&Slcms_cie_de2000); | ||
| 179 | defsubr (&Slcms_cam02_ucs); | ||
| 180 | } | ||
| 181 | |||
| 182 | #endif /* HAVE_LCMS2 */ | ||
diff --git a/src/lisp.h b/src/lisp.h index 81f8d6a24b5..19594e7830d 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -4396,6 +4396,11 @@ extern void syms_of_xml (void); | |||
| 4396 | extern void xml_cleanup_parser (void); | 4396 | extern void xml_cleanup_parser (void); |
| 4397 | #endif | 4397 | #endif |
| 4398 | 4398 | ||
| 4399 | #ifdef HAVE_LCMS2 | ||
| 4400 | /* Defined in lcms.c. */ | ||
| 4401 | extern void syms_of_lcms2 (void); | ||
| 4402 | #endif | ||
| 4403 | |||
| 4399 | #ifdef HAVE_ZLIB | 4404 | #ifdef HAVE_ZLIB |
| 4400 | /* Defined in decompress.c. */ | 4405 | /* Defined in decompress.c. */ |
| 4401 | extern void syms_of_decompress (void); | 4406 | extern void syms_of_decompress (void); |
diff --git a/src/xfaces.c b/src/xfaces.c index 86bb9b0b496..32a5bd5f60b 100644 --- a/src/xfaces.c +++ b/src/xfaces.c | |||
| @@ -4088,12 +4088,14 @@ color_distance (XColor *x, XColor *y) | |||
| 4088 | } | 4088 | } |
| 4089 | 4089 | ||
| 4090 | 4090 | ||
| 4091 | DEFUN ("color-distance", Fcolor_distance, Scolor_distance, 2, 3, 0, | 4091 | DEFUN ("color-distance", Fcolor_distance, Scolor_distance, 2, 4, 0, |
| 4092 | doc: /* Return an integer distance between COLOR1 and COLOR2 on FRAME. | 4092 | doc: /* Return an integer distance between COLOR1 and COLOR2 on FRAME. |
| 4093 | COLOR1 and COLOR2 may be either strings containing the color name, | 4093 | COLOR1 and COLOR2 may be either strings containing the color name, |
| 4094 | or lists of the form (RED GREEN BLUE). | 4094 | or lists of the form (RED GREEN BLUE), each in the range 0 to 65535 inclusive. |
| 4095 | If FRAME is unspecified or nil, the current frame is used. */) | 4095 | If FRAME is unspecified or nil, the current frame is used. |
| 4096 | (Lisp_Object color1, Lisp_Object color2, Lisp_Object frame) | 4096 | If METRIC is unspecified or nil, a modified L*u*v* metric is used. */) |
| 4097 | (Lisp_Object color1, Lisp_Object color2, Lisp_Object frame, | ||
| 4098 | Lisp_Object metric) | ||
| 4097 | { | 4099 | { |
| 4098 | struct frame *f = decode_live_frame (frame); | 4100 | struct frame *f = decode_live_frame (frame); |
| 4099 | XColor cdef1, cdef2; | 4101 | XColor cdef1, cdef2; |
| @@ -4107,7 +4109,10 @@ If FRAME is unspecified or nil, the current frame is used. */) | |||
| 4107 | && defined_color (f, SSDATA (color2), &cdef2, false))) | 4109 | && defined_color (f, SSDATA (color2), &cdef2, false))) |
| 4108 | signal_error ("Invalid color", color2); | 4110 | signal_error ("Invalid color", color2); |
| 4109 | 4111 | ||
| 4110 | return make_number (color_distance (&cdef1, &cdef2)); | 4112 | if (NILP (metric)) |
| 4113 | return make_number (color_distance (&cdef1, &cdef2)); | ||
| 4114 | else | ||
| 4115 | return call2 (metric, color1, color2); | ||
| 4111 | } | 4116 | } |
| 4112 | 4117 | ||
| 4113 | 4118 | ||