LuvMnL.c revision 61b2299d
1/* $Xorg: LuvMnL.c,v 1.3 2000/08/17 19:44:41 cpqbld Exp $ */
2
3/*
4 * Code and supporting documentation (c) Copyright 1990 1991 Tektronix, Inc.
5 * 	All Rights Reserved
6 *
7 * This file is a component of an X Window System-specific implementation
8 * of XCMS based on the TekColor Color Management System.  Permission is
9 * hereby granted to use, copy, modify, sell, and otherwise distribute this
10 * software and its documentation for any purpose and without fee, provided
11 * that this copyright, permission, and disclaimer notice is reproduced in
12 * all copies of this software and in supporting documentation.  TekColor
13 * is a trademark of Tektronix, Inc.
14 *
15 * Tektronix makes no representation about the suitability of this software
16 * for any purpose.  It is provided "as is" and with all faults.
17 *
18 * TEKTRONIX DISCLAIMS ALL WARRANTIES APPLICABLE TO THIS SOFTWARE,
19 * INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE.  IN NO EVENT SHALL TEKTRONIX BE LIABLE FOR ANY
21 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
22 * RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN ACTION OF
23 * CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
24 * CONNECTION WITH THE USE OR THE PERFORMANCE OF THIS SOFTWARE.
25 *
26 *	NAME
27 *		CIELuvMnL.c
28 *
29 *	DESCRIPTION
30 *		Source for the XcmsCIELuvQueryMinL() gamut boundary
31 *		querying routine.
32 *
33 */
34/* $XFree86: xc/lib/X11/LuvMnL.c,v 1.3 2001/01/17 19:41:39 dawes Exp $ */
35
36#ifdef HAVE_CONFIG_H
37#include <config.h>
38#endif
39#include "Xlibint.h"
40#include "Xcmsint.h"
41#include <math.h>
42#include "Cv.h"
43
44/*
45 *	DEFINES
46 */
47#define MAXBISECTCOUNT	100
48#define EPS		(XcmsFloat)0.001
49#define START_L_STAR	(XcmsFloat)40.0
50
51
52/************************************************************************
53 *									*
54 *			 PUBLIC ROUTINES				*
55 *									*
56 ************************************************************************/
57
58/*
59 *	NAME
60 *		XcmsCIELuvQueryMinL - Compute max Lstar for a hue and chroma
61 *
62 *	SYNOPSIS
63 */
64Status
65XcmsCIELuvQueryMinL(
66    XcmsCCC ccc,
67    XcmsFloat hue_angle,	    /* hue angle in degrees */
68    XcmsFloat chroma,
69    XcmsColor *pColor_return)
70/*
71 *	DESCRIPTION
72 *		Return the maximum Lstar for a specified hue_angle and chroma.
73 *
74 *	ASSUMPTIONS
75 *		This routine assumes that the white point associated with
76 *		the color specification is the Screen White Point.  The
77 *		Screen White Point will also be associated with the
78 *		returned color specification.
79 *
80 *	RETURNS
81 *		XcmsFailure - Failure
82 *              XcmsSuccess - Succeeded with no modifications
83 *
84 */
85{
86    XcmsCCCRec	myCCC;
87    XcmsColor   max_lc, tmp, prev;
88    XcmsFloat   max_chroma, tmp_chroma;
89    XcmsFloat   hue, nT, nChroma, lastChroma, prevChroma;
90    XcmsFloat   rFactor;
91    XcmsRGBi    rgb_saved;
92    int         nCount, nMaxCount;
93
94    /*
95     * Check Arguments
96     */
97    if (ccc == NULL || pColor_return == NULL) {
98	return(XcmsFailure);
99    }
100
101    /* setup the CCC to use for the conversions. */
102    memcpy ((char *) &myCCC, (char *) ccc, sizeof(XcmsCCCRec));
103    myCCC.clientWhitePt.format = XcmsUndefinedFormat;
104    myCCC.gamutCompProc = (XcmsCompressionProc) NULL;
105
106    while (hue_angle < 0.0) {
107	hue_angle += 360.0;
108    }
109    while (hue_angle >= 360.0) {
110	hue_angle -= 360.0;
111    }
112
113    hue = radians(hue_angle);
114    tmp.spec.CIELuv.L_star = START_L_STAR;
115    tmp.spec.CIELuv.u_star = XCMS_CIEUSTAROFHUE(hue, chroma);
116    tmp.spec.CIELuv.v_star = XCMS_CIEVSTAROFHUE(hue, chroma);
117    tmp.pixel = pColor_return->pixel;
118    tmp.format = XcmsCIELuvFormat;
119
120    /* Step 1: Obtain the maximum L_star and chroma for this hue. */
121    if (_XcmsCIELuvQueryMaxLCRGB(&myCCC, hue, &max_lc, &rgb_saved)
122	    == XcmsFailure) {
123	return(XcmsFailure);
124    }
125
126    max_chroma = XCMS_CIELUV_PMETRIC_CHROMA(max_lc.spec.CIELuv.u_star,
127					    max_lc.spec.CIELuv.v_star);
128
129    if (max_chroma <= chroma) {
130	/*
131	 *  If the chroma is greater than the chroma for the
132	 *  maximum L/chroma point then the L_star is the
133	 *  the L_star for the maximum L_star/chroma point.
134	 *  This is an error but I return the best approximation I can.
135         *  Thus the inconsistency.
136	 */
137	memcpy ((char *) pColor_return, (char *) &max_lc, sizeof (XcmsColor));
138	return(XcmsSuccess);
139    }
140
141    /*
142     *  If the chroma is equal to the chroma for the
143     *  maximum L_star/chroma point then the L_star is the
144     *  the L_star for the maximum L* and chroma point.
145     */
146    /* if (max_chroma == chroma) {
147     *  memcpy ((char *) pColor_return, (char *) &max_lc, sizeof (XcmsColor));
148     *	return(XcmsSuccess);
149     *    }
150     */
151
152    /* must do a bisection here to compute the maximum L* */
153    /* save the structure input so that any elements that */
154    /* are not touched are recopied later in the routine. */
155    nChroma = chroma;
156    tmp_chroma = max_chroma;
157    lastChroma = -1.0;
158    nMaxCount = MAXBISECTCOUNT;
159    rFactor = 1.0;
160
161    for (nCount = 0; nCount < nMaxCount; nCount++) {
162	prevChroma = lastChroma;
163	lastChroma = tmp_chroma;
164	nT = (nChroma - max_chroma) / max_chroma * rFactor;
165	memcpy ((char *)&prev, (char *)&tmp, sizeof(XcmsColor));
166	tmp.spec.RGBi.red   = rgb_saved.red + (rgb_saved.red * nT);
167	tmp.spec.RGBi.green = rgb_saved.green + (rgb_saved.green * nT);
168	tmp.spec.RGBi.blue  = rgb_saved.blue + (rgb_saved.blue * nT);
169	tmp.format = XcmsRGBiFormat;
170
171	/* convert from RGB to CIELuv */
172	if (_XcmsConvertColorsWithWhitePt(&myCCC, &tmp,
173		ScreenWhitePointOfCCC(&myCCC), 1, XcmsCIELuvFormat,
174		(Bool *) NULL) == XcmsFailure) {
175	    return(XcmsFailure);
176	}
177
178	/* Now check the return against what is expected */
179	tmp_chroma = XCMS_CIELUV_PMETRIC_CHROMA(tmp.spec.CIELuv.u_star,
180						tmp.spec.CIELuv.v_star);
181	if (tmp_chroma <= chroma + EPS && tmp_chroma >= chroma - EPS) {
182	    /* Found It! */
183	    memcpy ((char *) pColor_return, (char *) &tmp, sizeof (XcmsColor));
184	    return(XcmsSuccess);
185	}
186	nChroma += chroma - tmp_chroma;
187	if (nChroma > max_chroma) {
188	    nChroma = max_chroma;
189	    rFactor *= 0.5;  /* selective relaxation employed */
190	} else if (nChroma < 0.0) {
191	    if (XCMS_FABS(lastChroma - chroma) <
192		XCMS_FABS(tmp_chroma - chroma)) {
193		memcpy ((char *)pColor_return, (char *)&prev,
194			sizeof(XcmsColor));
195	    } else {
196		memcpy ((char *)pColor_return, (char *)&tmp,
197 			sizeof(XcmsColor));
198	    }
199	    return(XcmsSuccess);
200	} else if (tmp_chroma <= prevChroma + EPS &&
201		   tmp_chroma >= prevChroma - EPS) {
202	    rFactor *= 0.5;  /* selective relaxation employed */
203	}
204    }
205
206    if (nCount >= nMaxCount) {
207	if (XCMS_FABS(lastChroma - chroma) <
208	    XCMS_FABS(tmp_chroma - chroma)) {
209		memcpy ((char *)pColor_return, (char *)&prev,
210 			sizeof(XcmsColor));
211	    } else {
212		memcpy ((char *)pColor_return, (char *)&tmp,
213 			sizeof(XcmsColor));
214	}
215    }
216    memcpy ((char *) pColor_return, (char *) &tmp, sizeof (XcmsColor));
217    return(XcmsSuccess);
218}
219