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