Luv.c revision 9c019ec5
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
56static int CIELuv_ParseString(register char *spec, XcmsColor *pColor);
57static 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     */
78static 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     */
87static 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     */
100XcmsColorSpace	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 */
123static int
124CIELuv_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 */
203static Status
204XcmsCIELuv_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 */
233Status
234XcmsCIELuvToCIEuvY(
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 */
325Status
326XcmsCIEuvYToCIELuv(
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