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