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 * CIEuvy.c
28 *
29 * DESCRIPTION
30 * This file contains routines that support the CIE u'v'Y
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 */
37
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41 #include <X11/Xos.h>
42 #include "Xlibint.h"
43 #include "Xcmsint.h"
44 #include "Cv.h"
45
46 #include <stdio.h>
47
48 /*
49 * FORWARD DECLARATIONS
50 */
51 static int CIEuvY_ParseString(register char *spec, XcmsColor *pColor);
52
53 /*
54 * DEFINES
55 * Internal definitions that need NOT be exported to any package
56 * or program using this package.
57 */
58 #ifdef DBL_EPSILON
59 # define XMY_DBL_EPSILON DBL_EPSILON
60 #else
61 # define XMY_DBL_EPSILON 0.00001
62 #endif
63
64
65 /*
66 * LOCAL VARIABLES
67 */
68
69 /*
70 * NULL terminated list of functions applied to get from CIEuvY to CIEXYZ
71 */
72 static XcmsConversionProc Fl_CIEuvY_to_CIEXYZ[] = {
73 XcmsCIEuvYToCIEXYZ,
74 NULL
75 };
76
77 /*
78 * NULL terminated list of functions applied to get from CIEXYZ to CIEuvY
79 */
80 static XcmsConversionProc Fl_CIEXYZ_to_CIEuvY[] = {
81 XcmsCIEXYZToCIEuvY,
82 NULL
83 };
84
85
86 /*
87 * GLOBALS
88 */
89
90 /*
91 * CIE uvY Color Space
92 */
93 XcmsColorSpace XcmsCIEuvYColorSpace =
94 {
95 _XcmsCIEuvY_prefix, /* prefix */
96 XcmsCIEuvYFormat, /* id */
97 CIEuvY_ParseString, /* parseString */
98 Fl_CIEuvY_to_CIEXYZ, /* to_CIEXYZ */
99 Fl_CIEXYZ_to_CIEuvY, /* from_CIEXYZ */
100 1
101 };
102
103
104
105 /************************************************************************
106 * *
107 * PRIVATE ROUTINES *
108 * *
109 ************************************************************************/
110
111 /*
112 * NAME
113 * CIEuvY_ParseString
114 *
115 * SYNOPSIS
116 */
117 static int
CIEuvY_ParseString(register char * spec,XcmsColor * pColor)118 CIEuvY_ParseString(
119 register char *spec,
120 XcmsColor *pColor)
121 /*
122 * DESCRIPTION
123 * This routines takes a string and attempts to convert
124 * it into a XcmsColor structure with XcmsCIEuvYFormat.
125 * The assumed CIEuvY string syntax is:
126 * CIEuvY:<u>/<v>/<Y>
127 * Where u, v, and Y are in string input format for floats
128 * consisting of:
129 * a. an optional sign
130 * b. a string of numbers possibly containing a decimal point,
131 * c. an optional exponent field containing an 'E' or 'e'
132 * followed by a possibly signed integer string.
133 *
134 * RETURNS
135 * 0 if failed, non-zero otherwise.
136 */
137 {
138 size_t n;
139 char *pchar;
140
141 if ((pchar = strchr(spec, ':')) == NULL) {
142 return(XcmsFailure);
143 }
144 n = (size_t)(pchar - spec);
145
146 /*
147 * Check for proper prefix.
148 */
149 if (strncmp(spec, _XcmsCIEuvY_prefix, n) != 0) {
150 return(XcmsFailure);
151 }
152
153 /*
154 * Attempt to parse the value portion.
155 */
156 if (sscanf(spec + n + 1, "%lf/%lf/%lf",
157 &pColor->spec.CIEuvY.u_prime,
158 &pColor->spec.CIEuvY.v_prime,
159 &pColor->spec.CIEuvY.Y) != 3) {
160 char *s; /* Maybe failed due to locale */
161 int f;
162 if ((s = strdup(spec))) {
163 for (f = 0; s[f]; ++f)
164 if (s[f] == '.')
165 s[f] = ',';
166 else if (s[f] == ',')
167 s[f] = '.';
168 if (sscanf(s + n + 1, "%lf/%lf/%lf",
169 &pColor->spec.CIEuvY.u_prime,
170 &pColor->spec.CIEuvY.v_prime,
171 &pColor->spec.CIEuvY.Y) != 3) {
172 free(s);
173 return(XcmsFailure);
174 }
175 free(s);
176 } else
177 return(XcmsFailure);
178 }
179 pColor->format = XcmsCIEuvYFormat;
180 pColor->pixel = 0;
181 return(_XcmsCIEuvY_ValidSpec(pColor));
182 }
183
184
185 /************************************************************************
186 * *
187 * PUBLIC ROUTINES *
188 * *
189 ************************************************************************/
190
191 /*
192 * NAME
193 * XcmsCIEuvY_ValidSpec
194 *
195 * SYNOPSIS
196 */
197 Status
_XcmsCIEuvY_ValidSpec(XcmsColor * pColor)198 _XcmsCIEuvY_ValidSpec(
199 XcmsColor *pColor)
200 /*
201 * DESCRIPTION
202 * Checks if color specification valid for CIE u'v'Y.
203 *
204 * RETURNS
205 * XcmsFailure if invalid,
206 * XcmsSuccess if valid.
207 *
208 */
209 {
210 if (pColor->format != XcmsCIEuvYFormat
211 ||
212 (pColor->spec.CIEuvY.Y < 0.0 - XMY_DBL_EPSILON)
213 ||
214 (pColor->spec.CIEuvY.Y > 1.0 + XMY_DBL_EPSILON)) {
215 return(XcmsFailure);
216 }
217 return(XcmsSuccess);
218 }
219
220
221 /*
222 * NAME
223 * XcmsCIEuvYToCIEXYZ - convert CIEuvY to CIEXYZ
224 *
225 * SYNOPSIS
226 */
227 Status
XcmsCIEuvYToCIEXYZ(XcmsCCC ccc,XcmsColor * puvY_WhitePt,XcmsColor * pColors_in_out,unsigned int nColors)228 XcmsCIEuvYToCIEXYZ(
229 XcmsCCC ccc,
230 XcmsColor *puvY_WhitePt,
231 XcmsColor *pColors_in_out,
232 unsigned int nColors)
233 /*
234 * DESCRIPTION
235 * Converts color specifications in an array of XcmsColor
236 * structures from CIEuvY format to CIEXYZ format.
237 *
238 * RETURNS
239 * XcmsFailure if failed,
240 * XcmsSuccess if succeeded.
241 *
242 */
243 {
244 XcmsCIEXYZ XYZ_return;
245 XcmsColor whitePt;
246 unsigned int i;
247 XcmsColor *pColor = pColors_in_out;
248 XcmsFloat div, x, y, z, Y;
249
250 /*
251 * Check arguments
252 * Postpone checking puvY_WhitePt until it is actually needed
253 * otherwise converting between XYZ and uvY will fail.
254 */
255 if (pColors_in_out == NULL) {
256 return(XcmsFailure);
257 }
258
259
260 /*
261 * Now convert each XcmsColor structure to CIEXYZ form
262 */
263 for (i = 0; i < nColors; i++, pColor++) {
264
265 /* Make sure original format is CIEuvY */
266 if (!_XcmsCIEuvY_ValidSpec(pColor)) {
267 return(XcmsFailure);
268 }
269
270 /*
271 * Convert to CIEXYZ
272 */
273
274 Y = pColor->spec.CIEuvY.Y;
275
276 /* Convert color u'v' to xyz space */
277 div = (6.0 * pColor->spec.CIEuvY.u_prime) - (16.0 * pColor->spec.CIEuvY.v_prime) + 12.0;
278 if (div == 0.0) {
279 /* use white point since div == 0 */
280 if (puvY_WhitePt == NULL ) {
281 return(XcmsFailure);
282 }
283 /*
284 * Make sure white point is in CIEuvY form
285 */
286 if (puvY_WhitePt->format != XcmsCIEuvYFormat) {
287 /* Make copy of the white point because we're going to modify it */
288 memcpy((char *)&whitePt, (char *)puvY_WhitePt, sizeof(XcmsColor));
289 if (!_XcmsDIConvertColors(ccc, &whitePt, (XcmsColor *)NULL, 1,
290 XcmsCIEuvYFormat)) {
291 return(XcmsFailure);
292 }
293 puvY_WhitePt = &whitePt;
294 }
295 /* Make sure it is a white point, i.e., Y == 1.0 */
296 if (puvY_WhitePt->spec.CIEuvY.Y != 1.0) {
297 return(XcmsFailure);
298 }
299 div = (6.0 * puvY_WhitePt->spec.CIEuvY.u_prime) -
300 (16.0 * puvY_WhitePt->spec.CIEuvY.v_prime) + 12.0;
301 if (div == 0) {
302 /* internal error */
303 return(XcmsFailure);
304 }
305 x = 9.0 * puvY_WhitePt->spec.CIEuvY.u_prime / div;
306 y = 4.0 * puvY_WhitePt->spec.CIEuvY.v_prime / div;
307 } else {
308 x = 9.0 * pColor->spec.CIEuvY.u_prime / div;
309 y = 4.0 * pColor->spec.CIEuvY.v_prime / div;
310 }
311 z = 1.0 - x - y;
312
313 /* Convert from xyz to XYZ */
314 /* Conversion uses color normalized lightness based on Y */
315 if (y != 0.0) {
316 XYZ_return.X = x * Y / y;
317 } else {
318 XYZ_return.X = x;
319 }
320 XYZ_return.Y = Y;
321 if (y != 0.0) {
322 XYZ_return.Z = z * Y / y;
323 } else {
324 XYZ_return.Z = z;
325 }
326
327 memcpy((char *)&pColor->spec.CIEXYZ, (char *)&XYZ_return, sizeof(XcmsCIEXYZ));
328 /* Identify that format is now CIEXYZ */
329 pColor->format = XcmsCIEXYZFormat;
330 }
331
332 return(XcmsSuccess);
333 }
334
335
336 /*
337 * NAME
338 * XcmsCIEXYZToCIEuvY - convert CIEXYZ to CIEuvY
339 *
340 * SYNOPSIS
341 */
342 Status
XcmsCIEXYZToCIEuvY(XcmsCCC ccc,XcmsColor * puvY_WhitePt,XcmsColor * pColors_in_out,unsigned int nColors)343 XcmsCIEXYZToCIEuvY(
344 XcmsCCC ccc,
345 XcmsColor *puvY_WhitePt,
346 XcmsColor *pColors_in_out,
347 unsigned int nColors)
348 /*
349 * DESCRIPTION
350 * Converts color specifications in an array of XcmsColor
351 * structures from CIEXYZ format to CIEuvY format.
352 *
353 * RETURNS
354 * XcmsFailure if failed,
355 * XcmsSuccess if succeeded.
356 *
357 */
358 {
359 XcmsCIEuvY uvY_return;
360 XcmsColor whitePt;
361 unsigned int i;
362 XcmsColor *pColor = pColors_in_out;
363 XcmsFloat div;
364
365 /*
366 * Check arguments
367 * Postpone checking puvY_WhitePt until it is actually needed
368 * otherwise converting between XYZ and uvY will fail.
369 */
370 if (pColors_in_out == NULL) {
371 return(XcmsFailure);
372 }
373
374 /*
375 * Now convert each XcmsColor structure to CIEuvY form
376 */
377 for (i = 0; i < nColors; i++, pColor++) {
378
379 /* Make sure original format is CIEXYZ */
380 if (!_XcmsCIEXYZ_ValidSpec(pColor)) {
381 return(XcmsFailure);
382 }
383
384 /* Convert to CIEuvY */
385 div = pColor->spec.CIEXYZ.X + (15.0 * pColor->spec.CIEXYZ.Y) +
386 (3.0 * pColor->spec.CIEXYZ.Z);
387 if (div == 0.0) {
388 /* Use white point since div == 0.0 */
389 if (puvY_WhitePt == NULL ) {
390 return(XcmsFailure);
391 }
392 /*
393 * Make sure white point is in CIEuvY form
394 */
395 if (puvY_WhitePt->format != XcmsCIEuvYFormat) {
396 /* Make copy of the white point because we're going to modify it */
397 memcpy((char *)&whitePt, (char *)puvY_WhitePt, sizeof(XcmsColor));
398 if (!_XcmsDIConvertColors(ccc, &whitePt, (XcmsColor *)NULL, 1,
399 XcmsCIEuvYFormat)) {
400 return(XcmsFailure);
401 }
402 puvY_WhitePt = &whitePt;
403 }
404 /* Make sure it is a white point, i.e., Y == 1.0 */
405 if (puvY_WhitePt->spec.CIEuvY.Y != 1.0) {
406 return(XcmsFailure);
407 }
408 uvY_return.Y = pColor->spec.CIEXYZ.Y;
409 uvY_return.u_prime = puvY_WhitePt->spec.CIEuvY.u_prime;
410 uvY_return.v_prime = puvY_WhitePt->spec.CIEuvY.v_prime;
411 } else {
412 uvY_return.u_prime = 4.0 * pColor->spec.CIEXYZ.X / div;
413 uvY_return.v_prime = 9.0 * pColor->spec.CIEXYZ.Y / div;
414 uvY_return.Y = pColor->spec.CIEXYZ.Y;
415 }
416
417 memcpy((char *)&pColor->spec.CIEuvY, (char *)&uvY_return, sizeof(XcmsCIEuvY));
418 /* Identify that format is now CIEuvY */
419 pColor->format = XcmsCIEuvYFormat;
420 }
421
422 return(XcmsSuccess);
423 }
424