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 *		CIELabMnL.c
27 *
28 *	DESCRIPTION
29 *		Source for the XcmsCIELabQueryMinL() gamut boundary
30 *		querying routine.
31 *
32 */
33
34#ifdef HAVE_CONFIG_H
35#include <config.h>
36#endif
37#include "Xlibint.h"
38#include "Xcmsint.h"
39#include <math.h>
40#include "Cv.h"
41
42/*
43 *	DEFINES
44 */
45#define MAXBISECTCOUNT	100
46#define EPS		(XcmsFloat)0.001
47#define START_L_STAR	(XcmsFloat)40.0
48
49
50/************************************************************************
51 *									*
52 *			 PUBLIC ROUTINES				*
53 *									*
54 ************************************************************************/
55
56/*
57 *	NAME
58 *		XcmsCIELabQueryMinL - Compute max Lstar for a hue and chroma
59 *
60 *	SYNOPSIS
61 */
62Status
63XcmsCIELabQueryMinL(
64    XcmsCCC ccc,
65    XcmsFloat hue_angle,	    /* hue angle in degrees */
66    XcmsFloat chroma,
67    XcmsColor *pColor_return)
68/*
69 *	DESCRIPTION
70 *		Return the maximum Lstar for a specified hue_angle and chroma.
71 *
72 *	ASSUMPTIONS
73 *		This routine assumes that the white point associated with
74 *		the color specification is the Screen White Point.  The
75 *		Screen White Point will also be associated with the
76 *		returned color specification.
77 *
78 *	RETURNS
79 *		XcmsFailure - Failure
80 *              XcmsSuccess - Succeeded with no modifications
81 *
82 */
83{
84    XcmsCCCRec	myCCC;
85    XcmsColor   max_lc, tmp, prev;
86    XcmsFloat   max_chroma, tmp_chroma;
87    XcmsFloat   hue, nT, nChroma, lastChroma, prevChroma;
88    XcmsFloat   rFactor;
89    XcmsRGBi    rgb_saved;
90    int         nCount, nMaxCount;
91
92    /*
93     * Check Arguments
94     */
95    if (ccc == NULL || pColor_return == NULL) {
96	return(XcmsFailure);
97    }
98
99    /* setup the CCC to use for the conversions. */
100    memcpy ((char *) &myCCC, (char *) ccc, sizeof(XcmsCCCRec));
101    myCCC.clientWhitePt.format = XcmsUndefinedFormat;
102    myCCC.gamutCompProc = (XcmsCompressionProc) NULL;
103
104    while (hue_angle < 0.0) {
105	hue_angle += 360.0;
106    }
107    while (hue_angle >= 360.0) {
108	hue_angle -= 360.0;
109    }
110
111    hue = radians(hue_angle);
112    tmp.spec.CIELab.L_star = START_L_STAR;
113    tmp.spec.CIELab.a_star = XCMS_CIEASTAROFHUE(hue, chroma);
114    tmp.spec.CIELab.b_star = XCMS_CIEBSTAROFHUE(hue, chroma);
115    tmp.pixel = pColor_return->pixel;
116    tmp.format = XcmsCIELabFormat;
117
118    /* Step 1: Obtain the maximum L_star and chroma for this hue. */
119    if (_XcmsCIELabQueryMaxLCRGB(&myCCC, hue, &max_lc, &rgb_saved)
120	    == XcmsFailure) {
121	return(XcmsFailure);
122    }
123
124    max_chroma = XCMS_CIELAB_PMETRIC_CHROMA(max_lc.spec.CIELab.a_star,
125					    max_lc.spec.CIELab.b_star);
126
127    if (max_chroma <= chroma) {
128	/*
129	 *  If the chroma is greater than the chroma for the
130	 *  maximum L/chroma point then the L_star is the
131	 *  the L_star for the maximum L_star/chroma point.
132	 *  This is an error but I return the best approximation I can.
133         *  Thus the inconsistency.
134	 */
135	memcpy ((char *) pColor_return, (char *) &max_lc, sizeof (XcmsColor));
136	return(XcmsSuccess);
137    }
138
139    /*
140     *  If the chroma is equal to the chroma for the
141     *  maximum L_star/chroma point then the L_star is the
142     *  the L_star for the maximum L* and chroma point.
143     */
144    /* if (max_chroma == chroma) {
145     *  memcpy ((char *) pColor_return, (char *) &max_lc, sizeof (XcmsColor));
146     *	return(XcmsSuccess);
147     *    }
148     */
149
150    /* must do a bisection here to compute the maximum L* */
151    /* save the structure input so that any elements that */
152    /* are not touched are recopied later in the routine. */
153    nChroma = chroma;
154    tmp_chroma = max_chroma;
155    lastChroma = -1.0;
156    nMaxCount = MAXBISECTCOUNT;
157    rFactor = 1.0;
158
159    for (nCount = 0; nCount < nMaxCount; nCount++) {
160	prevChroma = lastChroma;
161	lastChroma = tmp_chroma;
162	nT = (nChroma - max_chroma) / max_chroma * rFactor;
163	memcpy ((char *)&prev, (char *)&tmp, sizeof(XcmsColor));
164	tmp.spec.RGBi.red   = rgb_saved.red + (rgb_saved.red * nT);
165	tmp.spec.RGBi.green = rgb_saved.green + (rgb_saved.green * nT);
166	tmp.spec.RGBi.blue  = rgb_saved.blue + (rgb_saved.blue * nT);
167	tmp.format = XcmsRGBiFormat;
168
169	/* convert from RGB to CIELab */
170	if (_XcmsConvertColorsWithWhitePt(&myCCC, &tmp,
171		ScreenWhitePointOfCCC(&myCCC), 1, XcmsCIELabFormat,
172		(Bool *) NULL) == XcmsFailure) {
173	    return(XcmsFailure);
174	}
175
176	/* Now check the return against what is expected */
177	tmp_chroma = XCMS_CIELAB_PMETRIC_CHROMA(tmp.spec.CIELab.a_star,
178						tmp.spec.CIELab.b_star);
179	if (tmp_chroma <= chroma + EPS && tmp_chroma >= chroma - EPS) {
180	    /* Found It! */
181	    memcpy ((char *) pColor_return, (char *) &tmp, sizeof (XcmsColor));
182	    return(XcmsSuccess);
183	}
184	nChroma += chroma - tmp_chroma;
185	if (nChroma > max_chroma) {
186	    nChroma = max_chroma;
187	    rFactor *= 0.5;  /* selective relaxation employed */
188	} else if (nChroma < 0.0) {
189	    if (XCMS_FABS(lastChroma - chroma) <
190		XCMS_FABS(tmp_chroma - chroma)) {
191		memcpy ((char *)pColor_return, (char *)&prev,
192			sizeof(XcmsColor));
193	    } else {
194		memcpy ((char *)pColor_return, (char *)&tmp,
195 			sizeof(XcmsColor));
196	    }
197	    return(XcmsSuccess);
198	} else if (tmp_chroma <= prevChroma + EPS &&
199		   tmp_chroma >= prevChroma - EPS) {
200	    rFactor *= 0.5;  /* selective relaxation employed */
201	}
202    }
203
204    if (nCount >= nMaxCount) {
205	if (XCMS_FABS(lastChroma - chroma) <
206	    XCMS_FABS(tmp_chroma - chroma)) {
207		memcpy ((char *)pColor_return, (char *)&prev,
208 			sizeof(XcmsColor));
209	    } else {
210		memcpy ((char *)pColor_return, (char *)&tmp,
211 			sizeof(XcmsColor));
212	}
213    }
214    memcpy ((char *) pColor_return, (char *) &tmp, sizeof (XcmsColor));
215    return(XcmsSuccess);
216}
217