1
2 /*
3 * Code and supporting documentation (c) Copyright 1990 1991 Tektronix, Inc.
4 * All Rights Reserved
5 *
6 * This file is a component of an X Window System-specific implementation
7 * of XCMS based on the TekColor Color Management System. Permission is
8 * hereby granted to use, copy, modify, sell, and otherwise distribute this
9 * software and its documentation for any purpose and without fee, provided
10 * that this copyright, permission, and disclaimer notice is reproduced in
11 * all copies of this software and in supporting documentation. TekColor
12 * is a trademark of Tektronix, Inc.
13 *
14 * Tektronix makes no representation about the suitability of this software
15 * for any purpose. It is provided "as is" and with all faults.
16 *
17 * TEKTRONIX DISCLAIMS ALL WARRANTIES APPLICABLE TO THIS SOFTWARE,
18 * INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
19 * PARTICULAR PURPOSE. IN NO EVENT SHALL TEKTRONIX BE LIABLE FOR ANY
20 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
21 * RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN ACTION OF
22 * CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
23 * CONNECTION WITH THE USE OR THE PERFORMANCE OF THIS SOFTWARE.
24 *
25 *
26 * NAME
27 * CIELuv.c
28 *
29 * DESCRIPTION
30 * This file contains routines that support the CIE L*u*v*
31 * color space to include conversions to and from the CIE
32 * XYZ space.
33 *
34 * DOCUMENTATION
35 * "TekColor Color Management System, System Implementor's Manual"
36 * and
37 * Fred W. Billmeyer & Max Saltzman, "Principles of Color
38 * Technology", John Wily & Sons, Inc, 1981.
39 */
40
41 #ifdef HAVE_CONFIG_H
42 #include <config.h>
43 #endif
44 #include <X11/Xos.h>
45 #include "Xlibint.h"
46 #include "Xcmsint.h"
47 #include "Cv.h"
48
49 #include <stdio.h> /* sscanf */
50
51
52 /*
53 * FORWARD DECLARATIONS
54 */
55
56 static int CIELuv_ParseString(register char *spec, XcmsColor *pColor);
57 static Status XcmsCIELuv_ValidSpec(XcmsColor *pColor);
58
59 /*
60 * DEFINES
61 * Internal definitions that need NOT be exported to any package
62 * or program using this package.
63 */
64 #ifdef DBL_EPSILON
65 # define XMY_DBL_EPSILON DBL_EPSILON
66 #else
67 # define XMY_DBL_EPSILON 0.00001
68 #endif
69
70
71 /*
72 * LOCAL VARIABLES
73 */
74
75 /*
76 * NULL terminated list of functions applied to get from CIELuv to CIEXYZ
77 */
78 static XcmsConversionProc Fl_CIELuv_to_CIEXYZ[] = {
79 XcmsCIELuvToCIEuvY,
80 XcmsCIEuvYToCIEXYZ,
81 NULL
82 };
83
84 /*
85 * NULL terminated list of functions applied to get from CIEXYZ to CIELuv
86 */
87 static XcmsConversionProc Fl_CIEXYZ_to_CIELuv[] = {
88 XcmsCIEXYZToCIEuvY,
89 XcmsCIEuvYToCIELuv,
90 NULL
91 };
92
93 /*
94 * GLOBALS
95 */
96
97 /*
98 * CIE Luv Color Space
99 */
100 XcmsColorSpace XcmsCIELuvColorSpace =
101 {
102 _XcmsCIELuv_prefix, /* prefix */
103 XcmsCIELuvFormat, /* id */
104 CIELuv_ParseString, /* parseString */
105 Fl_CIELuv_to_CIEXYZ, /* to_CIEXYZ */
106 Fl_CIEXYZ_to_CIELuv, /* from_CIEXYZ */
107 1
108 };
109
110
111 /************************************************************************
112 * *
113 * PRIVATE ROUTINES *
114 * *
115 ************************************************************************/
116
117 /*
118 * NAME
119 * CIELuv_ParseString
120 *
121 * SYNOPSIS
122 */
123 static int
CIELuv_ParseString(register char * spec,XcmsColor * pColor)124 CIELuv_ParseString(
125 register char *spec,
126 XcmsColor *pColor)
127 /*
128 * DESCRIPTION
129 * This routines takes a string and attempts to convert
130 * it into a XcmsColor structure with XcmsCIELuvFormat.
131 * The assumed CIELuv string syntax is:
132 * CIELuv:<L>/<u>/<v>
133 * Where L, u, and v are in string input format for floats
134 * consisting of:
135 * a. an optional sign
136 * b. a string of numbers possibly containing a decimal point,
137 * c. an optional exponent field containing an 'E' or 'e'
138 * followed by a possibly signed integer string.
139 *
140 * RETURNS
141 * 0 if failed, non-zero otherwise.
142 */
143 {
144 int n;
145 char *pchar;
146
147 if ((pchar = strchr(spec, ':')) == NULL) {
148 return(XcmsFailure);
149 }
150 n = (int)(pchar - spec);
151
152 /*
153 * Check for proper prefix.
154 */
155 if (strncmp(spec, _XcmsCIELuv_prefix, (size_t)n) != 0) {
156 return(XcmsFailure);
157 }
158
159 /*
160 * Attempt to parse the value portion.
161 */
162 if (sscanf(spec + n + 1, "%lf/%lf/%lf",
163 &pColor->spec.CIELuv.L_star,
164 &pColor->spec.CIELuv.u_star,
165 &pColor->spec.CIELuv.v_star) != 3) {
166 char *s; /* Maybe failed due to locale */
167 int f;
168 if ((s = strdup(spec))) {
169 for (f = 0; s[f]; ++f)
170 if (s[f] == '.')
171 s[f] = ',';
172 else if (s[f] == ',')
173 s[f] = '.';
174 if (sscanf(s + n + 1, "%lf/%lf/%lf",
175 &pColor->spec.CIELuv.L_star,
176 &pColor->spec.CIELuv.u_star,
177 &pColor->spec.CIELuv.v_star) != 3) {
178 free(s);
179 return(XcmsFailure);
180 }
181 free(s);
182 } else
183 return(XcmsFailure);
184 }
185 pColor->format = XcmsCIELuvFormat;
186 pColor->pixel = 0;
187 return(XcmsCIELuv_ValidSpec(pColor));
188 }
189
190
191 /************************************************************************
192 * *
193 * PUBLIC ROUTINES *
194 * *
195 ************************************************************************/
196
197 /*
198 * NAME
199 * XcmsCIELuv_ValidSpec
200 *
201 * SYNOPSIS
202 */
203 static Status
XcmsCIELuv_ValidSpec(XcmsColor * pColor)204 XcmsCIELuv_ValidSpec(
205 XcmsColor *pColor)
206 /*
207 * DESCRIPTION
208 * Checks if color specification valid for CIE L*u*v*.
209 *
210 * RETURNS
211 * XcmsFailure if invalid,
212 * XcmsSuccess if valid.
213 *
214 */
215 {
216 if (pColor->format != XcmsCIELuvFormat
217 ||
218 (pColor->spec.CIELuv.L_star < 0.0 - XMY_DBL_EPSILON)
219 ||
220 (pColor->spec.CIELuv.L_star > 100.0 + XMY_DBL_EPSILON)) {
221 return(XcmsFailure);
222 }
223 return(XcmsSuccess);
224 }
225
226
227 /*
228 * NAME
229 * XcmsCIELuvToCIEuvY - convert CIELuv to CIEuvY
230 *
231 * SYNOPSIS
232 */
233 Status
XcmsCIELuvToCIEuvY(XcmsCCC ccc,XcmsColor * pLuv_WhitePt,XcmsColor * pColors_in_out,unsigned int nColors)234 XcmsCIELuvToCIEuvY(
235 XcmsCCC ccc,
236 XcmsColor *pLuv_WhitePt,
237 XcmsColor *pColors_in_out,
238 unsigned int nColors)
239 /*
240 * DESCRIPTION
241 * Converts color specifications in an array of XcmsColor
242 * structures from CIELuv format to CIEuvY format.
243 *
244 * RETURNS
245 * XcmsFailure if failed,
246 * XcmsSuccess if succeeded.
247 *
248 */
249 {
250 XcmsColor *pColor = pColors_in_out;
251 XcmsColor whitePt;
252 XcmsCIEuvY uvY_return;
253 XcmsFloat tmpVal;
254 unsigned int i;
255
256 /*
257 * Check arguments
258 */
259 if (pLuv_WhitePt == NULL || pColors_in_out == NULL) {
260 return(XcmsFailure);
261 }
262
263 /*
264 * Make sure white point is in CIEuvY form
265 */
266 if (pLuv_WhitePt->format != XcmsCIEuvYFormat) {
267 /* Make copy of the white point because we're going to modify it */
268 memcpy((char *)&whitePt, (char *)pLuv_WhitePt, sizeof(XcmsColor));
269 if (!_XcmsDIConvertColors(ccc, &whitePt, (XcmsColor *)NULL,
270 1, XcmsCIEuvYFormat)) {
271 return(XcmsFailure);
272 }
273 pLuv_WhitePt = &whitePt;
274 }
275 /* Make sure it is a white point, i.e., Y == 1.0 */
276 if (pLuv_WhitePt->spec.CIEuvY.Y != 1.0) {
277 return(XcmsFailure);
278 }
279
280 /*
281 * Now convert each XcmsColor structure to CIEXYZ form
282 */
283 for (i = 0; i < nColors; i++, pColor++) {
284
285 /* Make sure original format is CIELuv and is valid */
286 if (!XcmsCIELuv_ValidSpec(pColor)) {
287 return(XcmsFailure);
288 }
289
290 if (pColor->spec.CIELuv.L_star < 7.99953624) {
291 uvY_return.Y = pColor->spec.CIELuv.L_star / 903.29;
292 } else {
293 tmpVal = (pColor->spec.CIELuv.L_star + 16.0) / 116.0;
294 uvY_return.Y = tmpVal * tmpVal * tmpVal; /* tmpVal ** 3 */
295 }
296
297
298
299 if (pColor->spec.CIELuv.L_star == 0.0) {
300 uvY_return.u_prime = pLuv_WhitePt->spec.CIEuvY.u_prime;
301 uvY_return.v_prime = pLuv_WhitePt->spec.CIEuvY.v_prime;
302 } else {
303 tmpVal = 13.0 * (pColor->spec.CIELuv.L_star / 100.0);
304 uvY_return.u_prime = pColor->spec.CIELuv.u_star/tmpVal +
305 pLuv_WhitePt->spec.CIEuvY.u_prime;
306 uvY_return.v_prime = pColor->spec.CIELuv.v_star/tmpVal +
307 pLuv_WhitePt->spec.CIEuvY.v_prime;
308 }
309 /* Copy result to pColor */
310 memcpy((char *)&pColor->spec, (char *)&uvY_return, sizeof(XcmsCIEuvY));
311
312 /* Identify that the format is now CIEuvY */
313 pColor->format = XcmsCIEuvYFormat;
314 }
315 return(XcmsSuccess);
316 }
317
318
319 /*
320 * NAME
321 * XcmsCIEuvYToCIELuv - convert CIEuvY to CIELuv
322 *
323 * SYNOPSIS
324 */
325 Status
XcmsCIEuvYToCIELuv(XcmsCCC ccc,XcmsColor * pLuv_WhitePt,XcmsColor * pColors_in_out,unsigned int nColors)326 XcmsCIEuvYToCIELuv(
327 XcmsCCC ccc,
328 XcmsColor *pLuv_WhitePt,
329 XcmsColor *pColors_in_out,
330 unsigned int nColors)
331 /*
332 * DESCRIPTION
333 * Converts color specifications in an array of XcmsColor
334 * structures from CIEuvY format to CIELab format.
335 *
336 * RETURNS
337 * XcmsFailure if failed,
338 * XcmsSuccess if succeeded.
339 *
340 */
341 {
342 XcmsColor *pColor = pColors_in_out;
343 XcmsColor whitePt;
344 XcmsCIELuv Luv_return;
345 XcmsFloat tmpVal;
346 unsigned int i;
347
348 /*
349 * Check arguments
350 */
351 if (pLuv_WhitePt == NULL || pColors_in_out == NULL) {
352 return(XcmsFailure);
353 }
354
355 /*
356 * Make sure white point is in CIEuvY form
357 */
358 if (pLuv_WhitePt->format != XcmsCIEuvYFormat) {
359 /* Make copy of the white point because we're going to modify it */
360 memcpy((char *)&whitePt, (char *)pLuv_WhitePt, sizeof(XcmsColor));
361 if (!_XcmsDIConvertColors(ccc, &whitePt,
362 (XcmsColor *)NULL, 1, XcmsCIEuvYFormat)) {
363 return(XcmsFailure);
364 }
365 pLuv_WhitePt = &whitePt;
366 }
367 /* Make sure it is a white point, i.e., Y == 1.0 */
368 if (pLuv_WhitePt->spec.CIEuvY.Y != 1.0) {
369 return(XcmsFailure);
370 }
371
372 /*
373 * Now convert each XcmsColor structure to CIEXYZ form
374 */
375 for (i = 0; i < nColors; i++, pColor++) {
376
377 if (!_XcmsCIEuvY_ValidSpec(pColor)) {
378 return(XcmsFailure);
379 }
380
381 /* Now convert the uvY to Luv */
382 Luv_return.L_star =
383 (pColor->spec.CIEuvY.Y < 0.008856)
384 ?
385 (pColor->spec.CIEuvY.Y * 903.29)
386 :
387 ((XcmsFloat)(XCMS_CUBEROOT(pColor->spec.CIEuvY.Y) * 116.0) - 16.0);
388 tmpVal = 13.0 * (Luv_return.L_star / 100.0);
389 Luv_return.u_star = tmpVal *
390 (pColor->spec.CIEuvY.u_prime - pLuv_WhitePt->spec.CIEuvY.u_prime);
391 Luv_return.v_star = tmpVal *
392 (pColor->spec.CIEuvY.v_prime - pLuv_WhitePt->spec.CIEuvY.v_prime);
393
394 /* Copy result to pColor */
395 memcpy((char *)&pColor->spec, (char *)&Luv_return, sizeof(XcmsCIELuv));
396
397 /* Identify that the format is now CIEuvY */
398 pColor->format = XcmsCIELuvFormat;
399 }
400 return(XcmsSuccess);
401 }
402