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