HVC.c revision 1ab64890
11ab64890Smrg/* $XdotOrg: lib/X11/src/xcms/HVC.c,v 1.4 2005-07-03 07:00:55 daniels Exp $ */
21ab64890Smrg/* $Xorg: HVC.c,v 1.3 2000/08/17 19:44:36 cpqbld Exp $ */
31ab64890Smrg
41ab64890Smrg/*
51ab64890Smrg * Code and supporting documentation (c) Copyright 1990 1991 Tektronix, Inc.
61ab64890Smrg * 	All Rights Reserved
71ab64890Smrg *
81ab64890Smrg * This file is a component of an X Window System-specific implementation
91ab64890Smrg * of Xcms based on the TekColor Color Management System.  TekColor is a
101ab64890Smrg * trademark of Tektronix, Inc.  The term "TekHVC" designates a particular
111ab64890Smrg * color space that is the subject of U.S. Patent No. 4,985,853 (equivalent
121ab64890Smrg * foreign patents pending).  Permission is hereby granted to use, copy,
131ab64890Smrg * modify, sell, and otherwise distribute this software and its
141ab64890Smrg * documentation for any purpose and without fee, provided that:
151ab64890Smrg *
161ab64890Smrg * 1. This copyright, permission, and disclaimer notice is reproduced in
171ab64890Smrg *    all copies of this software and any modification thereof and in
181ab64890Smrg *    supporting documentation;
191ab64890Smrg * 2. Any color-handling application which displays TekHVC color
201ab64890Smrg *    cooordinates identifies these as TekHVC color coordinates in any
211ab64890Smrg *    interface that displays these coordinates and in any associated
221ab64890Smrg *    documentation;
231ab64890Smrg * 3. The term "TekHVC" is always used, and is only used, in association
241ab64890Smrg *    with the mathematical derivations of the TekHVC Color Space,
251ab64890Smrg *    including those provided in this file and any equivalent pathways and
261ab64890Smrg *    mathematical derivations, regardless of digital (e.g., floating point
271ab64890Smrg *    or integer) representation.
281ab64890Smrg *
291ab64890Smrg * Tektronix makes no representation about the suitability of this software
301ab64890Smrg * for any purpose.  It is provided "as is" and with all faults.
311ab64890Smrg *
321ab64890Smrg * TEKTRONIX DISCLAIMS ALL WARRANTIES APPLICABLE TO THIS SOFTWARE,
331ab64890Smrg * INCLUDING THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
341ab64890Smrg * PARTICULAR PURPOSE.  IN NO EVENT SHALL TEKTRONIX BE LIABLE FOR ANY
351ab64890Smrg * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
361ab64890Smrg * RESULTING FROM LOSS OF USE, DATA, OR PROFITS, WHETHER IN AN ACTION OF
371ab64890Smrg * CONTRACT, NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
381ab64890Smrg * CONNECTION WITH THE USE OR THE PERFORMANCE OF THIS SOFTWARE.
391ab64890Smrg *
401ab64890Smrg *	NAME
411ab64890Smrg *		TekHVC.c
421ab64890Smrg *
431ab64890Smrg *	DESCRIPTION
441ab64890Smrg *		This file contains routines that support the TekHVC
451ab64890Smrg *		color space to include conversions to and from the CIE
461ab64890Smrg *		XYZ space.
471ab64890Smrg *
481ab64890Smrg *	DOCUMENTATION
491ab64890Smrg *		"TekColor Color Management System, System Implementor's Manual"
501ab64890Smrg */
511ab64890Smrg/* $XFree86: xc/lib/X11/HVC.c,v 1.3 2001/01/17 19:41:37 dawes Exp $ */
521ab64890Smrg
531ab64890Smrg#ifdef HAVE_CONFIG_H
541ab64890Smrg#include <config.h>
551ab64890Smrg#endif
561ab64890Smrg#include "Xlibint.h"
571ab64890Smrg#include "Xcmsint.h"
581ab64890Smrg#include <X11/Xos.h>
591ab64890Smrg#include <math.h>
601ab64890Smrg#include "Cv.h"
611ab64890Smrg
621ab64890Smrg#include <stdio.h>
631ab64890Smrg
641ab64890Smrg/*
651ab64890Smrg *	DEFINES
661ab64890Smrg */
671ab64890Smrg#define u_BR    0.7127          /* u' Best Red */
681ab64890Smrg#define v_BR    0.4931          /* v' Best Red */
691ab64890Smrg#define EPS     0.001
701ab64890Smrg#define CHROMA_SCALE_FACTOR   7.50725
711ab64890Smrg#ifndef PI
721ab64890Smrg#  ifdef M_PI
731ab64890Smrg#    define PI	M_PI
741ab64890Smrg#  else
751ab64890Smrg#    define PI       3.14159265358979323846264338327950
761ab64890Smrg#  endif
771ab64890Smrg#endif
781ab64890Smrg#ifndef degrees
791ab64890Smrg#  define degrees(r) ((XcmsFloat)(r) * 180.0 / PI)
801ab64890Smrg#endif /* degrees */
811ab64890Smrg#ifndef radians
821ab64890Smrg#  define radians(d) ((XcmsFloat)(d) * PI / 180.0)
831ab64890Smrg#endif /* radians */
841ab64890Smrg
851ab64890Smrg/*************************************************************************
861ab64890Smrg * Note: The DBL_EPSILON for ANSI is 1e-5 so my checks need to take
871ab64890Smrg *       this into account.  If your DBL_EPSILON is different then
881ab64890Smrg *       adjust this define.
891ab64890Smrg *
901ab64890Smrg *       Also note that EPS is the error factor in the calculations
911ab64890Smrg *       This may need to be the same as XMY_DBL_EPSILON in
921ab64890Smrg *       some implementations.
931ab64890Smrg **************************************************************************/
941ab64890Smrg#ifdef DBL_EPSILON
951ab64890Smrg#  define XMY_DBL_EPSILON DBL_EPSILON
961ab64890Smrg#else
971ab64890Smrg#  define XMY_DBL_EPSILON 0.00001
981ab64890Smrg#endif
991ab64890Smrg
1001ab64890Smrg/*
1011ab64890Smrg *	FORWARD DECLARATIONS
1021ab64890Smrg */
1031ab64890Smrgstatic int TekHVC_ParseString(register char *spec, XcmsColor *pColor);
1041ab64890Smrgstatic Status XcmsTekHVC_ValidSpec(XcmsColor *pColor);
1051ab64890Smrg
1061ab64890Smrg/*
1071ab64890Smrg *	LOCAL VARIABLES
1081ab64890Smrg */
1091ab64890Smrg
1101ab64890Smrg    /*
1111ab64890Smrg     * NULL terminated list of functions applied to get from TekHVC to CIEXYZ
1121ab64890Smrg     */
1131ab64890Smrgstatic XcmsConversionProc Fl_TekHVC_to_CIEXYZ[] = {
1141ab64890Smrg    XcmsTekHVCToCIEuvY,
1151ab64890Smrg    XcmsCIEuvYToCIEXYZ,
1161ab64890Smrg    NULL
1171ab64890Smrg};
1181ab64890Smrg
1191ab64890Smrg    /*
1201ab64890Smrg     * NULL terminated list of functions applied to get from CIEXYZ to TekHVC
1211ab64890Smrg     */
1221ab64890Smrgstatic XcmsConversionProc Fl_CIEXYZ_to_TekHVC[] = {
1231ab64890Smrg    XcmsCIEXYZToCIEuvY,
1241ab64890Smrg    XcmsCIEuvYToTekHVC,
1251ab64890Smrg    NULL
1261ab64890Smrg};
1271ab64890Smrg
1281ab64890Smrg/*
1291ab64890Smrg *	GLOBALS
1301ab64890Smrg */
1311ab64890Smrg
1321ab64890Smrg    /*
1331ab64890Smrg     * TekHVC Color Space
1341ab64890Smrg     */
1351ab64890SmrgXcmsColorSpace	XcmsTekHVCColorSpace =
1361ab64890Smrg    {
1371ab64890Smrg	_XcmsTekHVC_prefix,	/* prefix */
1381ab64890Smrg	XcmsTekHVCFormat,		/* id */
1391ab64890Smrg	TekHVC_ParseString,	/* parseString */
1401ab64890Smrg	Fl_TekHVC_to_CIEXYZ,	/* to_CIEXYZ */
1411ab64890Smrg	Fl_CIEXYZ_to_TekHVC,	/* from_CIEXYZ */
1421ab64890Smrg	1
1431ab64890Smrg    };
1441ab64890Smrg
1451ab64890Smrg
1461ab64890Smrg
1471ab64890Smrg
1481ab64890Smrg/************************************************************************
1491ab64890Smrg *									*
1501ab64890Smrg *			 PRIVATE ROUTINES				*
1511ab64890Smrg *									*
1521ab64890Smrg ************************************************************************/
1531ab64890Smrg
1541ab64890Smrg/*
1551ab64890Smrg *	NAME
1561ab64890Smrg *		TekHVC_ParseString
1571ab64890Smrg *
1581ab64890Smrg *	SYNOPSIS
1591ab64890Smrg */
1601ab64890Smrgstatic int
1611ab64890SmrgTekHVC_ParseString(
1621ab64890Smrg    register char *spec,
1631ab64890Smrg    XcmsColor *pColor)
1641ab64890Smrg/*
1651ab64890Smrg *	DESCRIPTION
1661ab64890Smrg *		This routines takes a string and attempts to convert
1671ab64890Smrg *		it into a XcmsColor structure with XcmsTekHVCFormat.
1681ab64890Smrg *		The assumed TekHVC string syntax is:
1691ab64890Smrg *		    TekHVC:<H>/<V>/<C>
1701ab64890Smrg *		Where H, V, and C are in string input format for floats
1711ab64890Smrg *		consisting of:
1721ab64890Smrg *		    a. an optional sign
1731ab64890Smrg *		    b. a string of numbers possibly containing a decimal point,
1741ab64890Smrg *		    c. an optional exponent field containing an 'E' or 'e'
1751ab64890Smrg *			followed by a possibly signed integer string.
1761ab64890Smrg *
1771ab64890Smrg *	RETURNS
1781ab64890Smrg *		XcmsFailure if invalid;
1791ab64890Smrg *		XcmsSuccess if valid.
1801ab64890Smrg */
1811ab64890Smrg{
1821ab64890Smrg    int n;
1831ab64890Smrg    char *pchar;
1841ab64890Smrg
1851ab64890Smrg    if ((pchar = strchr(spec, ':')) == NULL) {
1861ab64890Smrg	return(XcmsFailure);
1871ab64890Smrg    }
1881ab64890Smrg    n = (int)(pchar - spec);
1891ab64890Smrg
1901ab64890Smrg    /*
1911ab64890Smrg     * Check for proper prefix.
1921ab64890Smrg     */
1931ab64890Smrg    if (strncmp(spec, _XcmsTekHVC_prefix, n) != 0) {
1941ab64890Smrg	return(XcmsFailure);
1951ab64890Smrg    }
1961ab64890Smrg
1971ab64890Smrg    /*
1981ab64890Smrg     * Attempt to parse the value portion.
1991ab64890Smrg     */
2001ab64890Smrg    if (sscanf(spec + n + 1, "%lf/%lf/%lf",
2011ab64890Smrg	    &pColor->spec.TekHVC.H,
2021ab64890Smrg	    &pColor->spec.TekHVC.V,
2031ab64890Smrg	    &pColor->spec.TekHVC.C) != 3) {
2041ab64890Smrg        char *s; /* Maybe failed due to locale */
2051ab64890Smrg        int f;
2061ab64890Smrg        if ((s = strdup(spec))) {
2071ab64890Smrg            for (f = 0; s[f]; ++f)
2081ab64890Smrg                if (s[f] == '.')
2091ab64890Smrg                    s[f] = ',';
2101ab64890Smrg                else if (s[f] == ',')
2111ab64890Smrg                    s[f] = '.';
2121ab64890Smrg	    if (sscanf(s + n + 1, "%lf/%lf/%lf",
2131ab64890Smrg		       &pColor->spec.TekHVC.H,
2141ab64890Smrg		       &pColor->spec.TekHVC.V,
2151ab64890Smrg		       &pColor->spec.TekHVC.C) != 3) {
2161ab64890Smrg                free(s);
2171ab64890Smrg                return(XcmsFailure);
2181ab64890Smrg            }
2191ab64890Smrg            free(s);
2201ab64890Smrg        } else
2211ab64890Smrg	    return(XcmsFailure);
2221ab64890Smrg    }
2231ab64890Smrg    pColor->format = XcmsTekHVCFormat;
2241ab64890Smrg    pColor->pixel = 0;
2251ab64890Smrg    return(XcmsTekHVC_ValidSpec(pColor));
2261ab64890Smrg}
2271ab64890Smrg
2281ab64890Smrg
2291ab64890Smrg/*
2301ab64890Smrg *	NAME
2311ab64890Smrg *		ThetaOffset -- compute thetaOffset
2321ab64890Smrg *
2331ab64890Smrg *	SYNOPSIS
2341ab64890Smrg */
2351ab64890Smrgstatic int
2361ab64890SmrgThetaOffset(
2371ab64890Smrg    XcmsColor *pWhitePt,
2381ab64890Smrg    XcmsFloat *pThetaOffset)
2391ab64890Smrg/*
2401ab64890Smrg *	DESCRIPTION
2411ab64890Smrg *		This routine computes the theta offset of a given
2421ab64890Smrg *		white point, i.e. XcmsColor.  It is used in both this
2431ab64890Smrg *		conversion and the printer conversions.
2441ab64890Smrg *
2451ab64890Smrg *	RETURNS
2461ab64890Smrg *		0 if failed.
2471ab64890Smrg *		1 if succeeded with no modifications.
2481ab64890Smrg *
2491ab64890Smrg *	ASSUMPTIONS
2501ab64890Smrg *		Assumes:
2511ab64890Smrg *			pWhitePt != NULL
2521ab64890Smrg *			pWhitePt->format == XcmsCIEuvYFormat
2531ab64890Smrg *
2541ab64890Smrg */
2551ab64890Smrg{
2561ab64890Smrg    double div, slopeuv;
2571ab64890Smrg
2581ab64890Smrg    if (pWhitePt == NULL || pWhitePt->format != XcmsCIEuvYFormat) {
2591ab64890Smrg	return(0);
2601ab64890Smrg    }
2611ab64890Smrg
2621ab64890Smrg    if ((div = u_BR - pWhitePt->spec.CIEuvY.u_prime) == 0.0) {
2631ab64890Smrg	return(0);
2641ab64890Smrg    }
2651ab64890Smrg    slopeuv = (v_BR - pWhitePt->spec.CIEuvY.v_prime) / div;
2661ab64890Smrg    *pThetaOffset = degrees(XCMS_ATAN(slopeuv));
2671ab64890Smrg    return(1);
2681ab64890Smrg}
2691ab64890Smrg
2701ab64890Smrg
2711ab64890Smrg
2721ab64890Smrg/************************************************************************
2731ab64890Smrg *									*
2741ab64890Smrg *			 PUBLIC ROUTINES				*
2751ab64890Smrg *									*
2761ab64890Smrg ************************************************************************/
2771ab64890Smrg
2781ab64890Smrg/*
2791ab64890Smrg *	NAME
2801ab64890Smrg *		XcmsTekHVC_ValidSpec()
2811ab64890Smrg *
2821ab64890Smrg *	SYNOPSIS
2831ab64890Smrg */
2841ab64890Smrgstatic int
2851ab64890SmrgXcmsTekHVC_ValidSpec(
2861ab64890Smrg    XcmsColor *pColor)
2871ab64890Smrg/*
2881ab64890Smrg *	DESCRIPTION
2891ab64890Smrg *		Checks if values in the color specification are valid.
2901ab64890Smrg *		Also brings hue into the range 0.0 <= Hue < 360.0
2911ab64890Smrg *
2921ab64890Smrg *	RETURNS
2931ab64890Smrg *		0 if not valid.
2941ab64890Smrg *		1 if valid.
2951ab64890Smrg *
2961ab64890Smrg */
2971ab64890Smrg{
2981ab64890Smrg    if (pColor->format != XcmsTekHVCFormat) {
2991ab64890Smrg	return(XcmsFailure);
3001ab64890Smrg    }
3011ab64890Smrg    if (pColor->spec.TekHVC.V < (0.0 - XMY_DBL_EPSILON)
3021ab64890Smrg	    || pColor->spec.TekHVC.V > (100.0 + XMY_DBL_EPSILON)
3031ab64890Smrg	    || (pColor->spec.TekHVC.C < 0.0 - XMY_DBL_EPSILON)) {
3041ab64890Smrg	return(XcmsFailure);
3051ab64890Smrg    }
3061ab64890Smrg
3071ab64890Smrg    if (pColor->spec.TekHVC.V < 0.0) {
3081ab64890Smrg	    pColor->spec.TekHVC.V = 0.0 + XMY_DBL_EPSILON;
3091ab64890Smrg    } else if (pColor->spec.TekHVC.V > 100.0) {
3101ab64890Smrg	pColor->spec.TekHVC.V = 100.0 - XMY_DBL_EPSILON;
3111ab64890Smrg    }
3121ab64890Smrg
3131ab64890Smrg    if (pColor->spec.TekHVC.C < 0.0) {
3141ab64890Smrg	pColor->spec.TekHVC.C = 0.0 - XMY_DBL_EPSILON;
3151ab64890Smrg    }
3161ab64890Smrg
3171ab64890Smrg    while (pColor->spec.TekHVC.H < 0.0) {
3181ab64890Smrg	pColor->spec.TekHVC.H += 360.0;
3191ab64890Smrg    }
3201ab64890Smrg    while (pColor->spec.TekHVC.H >= 360.0) {
3211ab64890Smrg	pColor->spec.TekHVC.H -= 360.0;
3221ab64890Smrg    }
3231ab64890Smrg    return(XcmsSuccess);
3241ab64890Smrg}
3251ab64890Smrg
3261ab64890Smrg/*
3271ab64890Smrg *	NAME
3281ab64890Smrg *		XcmsTekHVCToCIEuvY - convert TekHVC to CIEuvY
3291ab64890Smrg *
3301ab64890Smrg *	SYNOPSIS
3311ab64890Smrg */
3321ab64890SmrgStatus
3331ab64890SmrgXcmsTekHVCToCIEuvY(
3341ab64890Smrg    XcmsCCC ccc,
3351ab64890Smrg    XcmsColor *pHVC_WhitePt,
3361ab64890Smrg    XcmsColor *pColors_in_out,
3371ab64890Smrg    unsigned int nColors)
3381ab64890Smrg/*
3391ab64890Smrg *	DESCRIPTION
3401ab64890Smrg *		Transforms an array of TekHVC color specifications, given
3411ab64890Smrg *		their associated white point, to CIECIEuvY.color
3421ab64890Smrg *		specifications.
3431ab64890Smrg *
3441ab64890Smrg *	RETURNS
3451ab64890Smrg *		XcmsFailure if failed, XcmsSuccess otherwise.
3461ab64890Smrg *
3471ab64890Smrg */
3481ab64890Smrg{
3491ab64890Smrg    XcmsFloat	thetaOffset;
3501ab64890Smrg    XcmsColor	*pColor = pColors_in_out;
3511ab64890Smrg    XcmsColor	whitePt;
3521ab64890Smrg    XcmsCIEuvY	uvY_return;
3531ab64890Smrg    XcmsFloat	tempHue, u, v;
3541ab64890Smrg    XcmsFloat	tmpVal;
3551ab64890Smrg    register int i;
3561ab64890Smrg
3571ab64890Smrg    /*
3581ab64890Smrg     * Check arguments
3591ab64890Smrg     */
3601ab64890Smrg    if (pHVC_WhitePt == NULL || pColors_in_out == NULL) {
3611ab64890Smrg	return(XcmsFailure);
3621ab64890Smrg    }
3631ab64890Smrg
3641ab64890Smrg    /*
3651ab64890Smrg     * Make sure white point is in CIEuvY form
3661ab64890Smrg     */
3671ab64890Smrg    if (pHVC_WhitePt->format != XcmsCIEuvYFormat) {
3681ab64890Smrg	/* Make copy of the white point because we're going to modify it */
3691ab64890Smrg	memcpy((char *)&whitePt, (char *)pHVC_WhitePt, sizeof(XcmsColor));
3701ab64890Smrg	if (!_XcmsDIConvertColors(ccc, &whitePt, (XcmsColor *)NULL, 1,
3711ab64890Smrg		XcmsCIEuvYFormat)) {
3721ab64890Smrg	    return(XcmsFailure);
3731ab64890Smrg	}
3741ab64890Smrg	pHVC_WhitePt = &whitePt;
3751ab64890Smrg    }
3761ab64890Smrg    /* Make sure it is a white point, i.e., Y == 1.0 */
3771ab64890Smrg    if (pHVC_WhitePt->spec.CIEuvY.Y != 1.0) {
3781ab64890Smrg	return(XcmsFailure);
3791ab64890Smrg    }
3801ab64890Smrg
3811ab64890Smrg    /* Get the thetaOffset */
3821ab64890Smrg    if (!ThetaOffset(pHVC_WhitePt, &thetaOffset)) {
3831ab64890Smrg	return(XcmsFailure);
3841ab64890Smrg    }
3851ab64890Smrg
3861ab64890Smrg    /*
3871ab64890Smrg     * Now convert each XcmsColor structure to CIEXYZ form
3881ab64890Smrg     */
3891ab64890Smrg    for (i = 0; i < nColors; i++, pColor++) {
3901ab64890Smrg
3911ab64890Smrg	/* Make sure original format is TekHVC and is valid */
3921ab64890Smrg	if (!XcmsTekHVC_ValidSpec(pColor)) {
3931ab64890Smrg	    return(XcmsFailure);
3941ab64890Smrg	}
3951ab64890Smrg
3961ab64890Smrg	if (pColor->spec.TekHVC.V == 0.0 || pColor->spec.TekHVC.V == 100.0) {
3971ab64890Smrg	    if (pColor->spec.TekHVC.V == 100.0) {
3981ab64890Smrg		uvY_return.Y = 1.0;
3991ab64890Smrg	    } else { /* pColor->spec.TekHVC.V == 0.0 */
4001ab64890Smrg		uvY_return.Y = 0.0;
4011ab64890Smrg	    }
4021ab64890Smrg	    uvY_return.u_prime = pHVC_WhitePt->spec.CIEuvY.u_prime;
4031ab64890Smrg	    uvY_return.v_prime = pHVC_WhitePt->spec.CIEuvY.v_prime;
4041ab64890Smrg	} else {
4051ab64890Smrg
4061ab64890Smrg	    /* Find the hue based on the white point offset */
4071ab64890Smrg	    tempHue = pColor->spec.TekHVC.H + thetaOffset;
4081ab64890Smrg
4091ab64890Smrg	    while (tempHue < 0.0) {
4101ab64890Smrg		tempHue += 360.0;
4111ab64890Smrg	    }
4121ab64890Smrg	    while (tempHue >= 360.0) {
4131ab64890Smrg		tempHue -= 360.0;
4141ab64890Smrg	    }
4151ab64890Smrg
4161ab64890Smrg	    tempHue = radians(tempHue);
4171ab64890Smrg
4181ab64890Smrg	    /* Calculate u'v' for the obtained hue */
4191ab64890Smrg	    u = (XcmsFloat) ((XCMS_COS(tempHue) * pColor->spec.TekHVC.C) /
4201ab64890Smrg		    (pColor->spec.TekHVC.V * (double)CHROMA_SCALE_FACTOR));
4211ab64890Smrg	    v = (XcmsFloat) ((XCMS_SIN(tempHue) * pColor->spec.TekHVC.C) /
4221ab64890Smrg		    (pColor->spec.TekHVC.V * (double)CHROMA_SCALE_FACTOR));
4231ab64890Smrg
4241ab64890Smrg	    /* Based on the white point get the offset from best red */
4251ab64890Smrg	    uvY_return.u_prime = u + pHVC_WhitePt->spec.CIEuvY.u_prime;
4261ab64890Smrg	    uvY_return.v_prime = v + pHVC_WhitePt->spec.CIEuvY.v_prime;
4271ab64890Smrg
4281ab64890Smrg	    /* Calculate the Y value based on the L* = V. */
4291ab64890Smrg	    if (pColor->spec.TekHVC.V < 7.99953624) {
4301ab64890Smrg		uvY_return.Y = pColor->spec.TekHVC.V / 903.29;
4311ab64890Smrg	    } else {
4321ab64890Smrg		tmpVal = (pColor->spec.TekHVC.V + 16.0) / 116.0;
4331ab64890Smrg		uvY_return.Y = tmpVal * tmpVal * tmpVal; /* tmpVal ** 3 */
4341ab64890Smrg	    }
4351ab64890Smrg	}
4361ab64890Smrg
4371ab64890Smrg	/* Copy result to pColor */
4381ab64890Smrg	memcpy((char *)&pColor->spec, (char *)&uvY_return, sizeof(XcmsCIEuvY));
4391ab64890Smrg
4401ab64890Smrg	/* Identify that the format is now CIEuvY */
4411ab64890Smrg	pColor->format = XcmsCIEuvYFormat;
4421ab64890Smrg    }
4431ab64890Smrg    return(XcmsSuccess);
4441ab64890Smrg}
4451ab64890Smrg
4461ab64890Smrg
4471ab64890Smrg/*
4481ab64890Smrg *	NAME
4491ab64890Smrg *		XcmsCIEuvYToTekHVC - convert CIEuvY to TekHVC
4501ab64890Smrg *
4511ab64890Smrg *	SYNOPSIS
4521ab64890Smrg */
4531ab64890SmrgStatus
4541ab64890SmrgXcmsCIEuvYToTekHVC(
4551ab64890Smrg    XcmsCCC ccc,
4561ab64890Smrg    XcmsColor *pHVC_WhitePt,
4571ab64890Smrg    XcmsColor *pColors_in_out,
4581ab64890Smrg    unsigned int nColors)
4591ab64890Smrg/*
4601ab64890Smrg *	DESCRIPTION
4611ab64890Smrg *		Transforms an array of CIECIEuvY.color specifications, given
4621ab64890Smrg *		their assiciated white point, to TekHVC specifications.
4631ab64890Smrg *
4641ab64890Smrg *	RETURNS
4651ab64890Smrg *		XcmsFailure if failed, XcmsSuccess otherwise.
4661ab64890Smrg *
4671ab64890Smrg */
4681ab64890Smrg{
4691ab64890Smrg    XcmsFloat	theta, L2, u, v, nThetaLow, nThetaHigh;
4701ab64890Smrg    XcmsFloat	thetaOffset;
4711ab64890Smrg    XcmsColor	*pColor = pColors_in_out;
4721ab64890Smrg    XcmsColor	whitePt;
4731ab64890Smrg    XcmsTekHVC	HVC_return;
4741ab64890Smrg    register int i;
4751ab64890Smrg
4761ab64890Smrg    /*
4771ab64890Smrg     * Check arguments
4781ab64890Smrg     */
4791ab64890Smrg    if (pHVC_WhitePt == NULL || pColors_in_out == NULL) {
4801ab64890Smrg	return(XcmsFailure);
4811ab64890Smrg    }
4821ab64890Smrg
4831ab64890Smrg    /*
4841ab64890Smrg     * Make sure white point is in CIEuvY form
4851ab64890Smrg     */
4861ab64890Smrg    if (pHVC_WhitePt->format != XcmsCIEuvYFormat) {
4871ab64890Smrg	/* Make copy of the white point because we're going to modify it */
4881ab64890Smrg	memcpy((char *)&whitePt, (char *)pHVC_WhitePt, sizeof(XcmsColor));
4891ab64890Smrg	if (!_XcmsDIConvertColors(ccc, &whitePt, (XcmsColor *)NULL, 1,
4901ab64890Smrg		XcmsCIEuvYFormat)) {
4911ab64890Smrg	    return(XcmsFailure);
4921ab64890Smrg	}
4931ab64890Smrg	pHVC_WhitePt = &whitePt;
4941ab64890Smrg    }
4951ab64890Smrg    /* Make sure it is a white point, i.e., Y == 1.0 */
4961ab64890Smrg    if (pHVC_WhitePt->spec.CIEuvY.Y != 1.0) {
4971ab64890Smrg	return(XcmsFailure);
4981ab64890Smrg    }
4991ab64890Smrg    if (!ThetaOffset(pHVC_WhitePt, &thetaOffset)) {
5001ab64890Smrg	return(XcmsFailure);
5011ab64890Smrg    }
5021ab64890Smrg
5031ab64890Smrg    /*
5041ab64890Smrg     * Now convert each XcmsColor structure to CIEXYZ form
5051ab64890Smrg     */
5061ab64890Smrg    for (i = 0; i < nColors; i++, pColor++) {
5071ab64890Smrg	if (!_XcmsCIEuvY_ValidSpec(pColor)) {
5081ab64890Smrg	    return(XcmsFailure);
5091ab64890Smrg	}
5101ab64890Smrg
5111ab64890Smrg	/* Use the white point offset to determine HVC */
5121ab64890Smrg	u = pColor->spec.CIEuvY.u_prime - pHVC_WhitePt->spec.CIEuvY.u_prime;
5131ab64890Smrg	v = pColor->spec.CIEuvY.v_prime - pHVC_WhitePt->spec.CIEuvY.v_prime;
5141ab64890Smrg
5151ab64890Smrg	/* Calculate the offset */
5161ab64890Smrg	if (u == 0.0) {
5171ab64890Smrg	    theta = 0.0;
5181ab64890Smrg	} else {
5191ab64890Smrg	    theta = v / u;
5201ab64890Smrg	    theta = (XcmsFloat) XCMS_ATAN((double)theta);
5211ab64890Smrg	    theta = degrees(theta);
5221ab64890Smrg	}
5231ab64890Smrg
5241ab64890Smrg	nThetaLow = 0.0;
5251ab64890Smrg	nThetaHigh = 360.0;
5261ab64890Smrg	if (u > 0.0 && v > 0.0) {
5271ab64890Smrg	    nThetaLow = 0.0;
5281ab64890Smrg	    nThetaHigh = 90.0;
5291ab64890Smrg	} else if (u < 0.0 && v > 0.0) {
5301ab64890Smrg	    nThetaLow = 90.0;
5311ab64890Smrg	    nThetaHigh = 180.0;
5321ab64890Smrg	} else if (u < 0.0 && v < 0.0) {
5331ab64890Smrg	    nThetaLow = 180.0;
5341ab64890Smrg	    nThetaHigh = 270.0;
5351ab64890Smrg	} else if (u > 0.0 && v < 0.0) {
5361ab64890Smrg	    nThetaLow = 270.0;
5371ab64890Smrg	    nThetaHigh = 360.0;
5381ab64890Smrg	}
5391ab64890Smrg	while (theta < nThetaLow) {
5401ab64890Smrg		theta += 90.0;
5411ab64890Smrg	}
5421ab64890Smrg	while (theta >= nThetaHigh) {
5431ab64890Smrg	    theta -= 90.0;
5441ab64890Smrg	}
5451ab64890Smrg
5461ab64890Smrg	/* calculate the L value from the given Y */
5471ab64890Smrg	L2 = (pColor->spec.CIEuvY.Y < 0.008856)
5481ab64890Smrg	    ?
5491ab64890Smrg	    (pColor->spec.CIEuvY.Y * 903.29)
5501ab64890Smrg	    :
5511ab64890Smrg	    ((XcmsFloat)(XCMS_CUBEROOT(pColor->spec.CIEuvY.Y) * 116.0) - 16.0);
5521ab64890Smrg	HVC_return.C = L2 * CHROMA_SCALE_FACTOR * XCMS_SQRT((double) ((u * u) + (v * v)));
5531ab64890Smrg	if (HVC_return.C < 0.0) {
5541ab64890Smrg	    theta = 0.0;
5551ab64890Smrg	}
5561ab64890Smrg	HVC_return.V = L2;
5571ab64890Smrg	HVC_return.H = theta - thetaOffset;
5581ab64890Smrg
5591ab64890Smrg	/*
5601ab64890Smrg	 * If this is within the error margin let some other routine later
5611ab64890Smrg	 * in the chain worry about the slop in the calculations.
5621ab64890Smrg	 */
5631ab64890Smrg	while (HVC_return.H < -EPS) {
5641ab64890Smrg	    HVC_return.H += 360.0;
5651ab64890Smrg	}
5661ab64890Smrg	while (HVC_return.H >= 360.0 + EPS) {
5671ab64890Smrg	    HVC_return.H -= 360.0;
5681ab64890Smrg	}
5691ab64890Smrg
5701ab64890Smrg	/* Copy result to pColor */
5711ab64890Smrg	memcpy((char *)&pColor->spec, (char *)&HVC_return, sizeof(XcmsTekHVC));
5721ab64890Smrg
5731ab64890Smrg	/* Identify that the format is now CIEuvY */
5741ab64890Smrg	pColor->format = XcmsTekHVCFormat;
5751ab64890Smrg    }
5761ab64890Smrg    return(XcmsSuccess);
5771ab64890Smrg}
5781ab64890Smrg
5791ab64890Smrg
5801ab64890Smrg/*
5811ab64890Smrg *	NAME
5821ab64890Smrg *		_XcmsTekHVC_CheckModify
5831ab64890Smrg *
5841ab64890Smrg *	SYNOPSIS
5851ab64890Smrg */
5861ab64890Smrgint
5871ab64890Smrg_XcmsTekHVC_CheckModify(
5881ab64890Smrg    XcmsColor *pColor)
5891ab64890Smrg/*
5901ab64890Smrg *	DESCRIPTION
5911ab64890Smrg *		Checks if values in the color specification are valid.
5921ab64890Smrg *		If they are not it modifies the values.
5931ab64890Smrg *		Also brings hue into the range 0.0 <= Hue < 360.0
5941ab64890Smrg *
5951ab64890Smrg *	RETURNS
5961ab64890Smrg *		0 if not valid.
5971ab64890Smrg *		1 if valid.
5981ab64890Smrg *
5991ab64890Smrg */
6001ab64890Smrg{
6011ab64890Smrg    int n;
6021ab64890Smrg
6031ab64890Smrg    /* For now only use the TekHVC numbers as inputs */
6041ab64890Smrg    if (pColor->format != XcmsTekHVCFormat) {
6051ab64890Smrg	return(0);
6061ab64890Smrg    }
6071ab64890Smrg
6081ab64890Smrg    if (pColor->spec.TekHVC.V < 0.0) {
6091ab64890Smrg	pColor->spec.TekHVC.V = 0.0 + XMY_DBL_EPSILON;
6101ab64890Smrg    } else if (pColor->spec.TekHVC.V > 100.0) {
6111ab64890Smrg	pColor->spec.TekHVC.V = 100.0 - XMY_DBL_EPSILON;
6121ab64890Smrg    }
6131ab64890Smrg
6141ab64890Smrg    if (pColor->spec.TekHVC.C < 0.0) {
6151ab64890Smrg	pColor->spec.TekHVC.C = 0.0 - XMY_DBL_EPSILON;
6161ab64890Smrg    }
6171ab64890Smrg
6181ab64890Smrg    if (pColor->spec.TekHVC.H < 0.0) {
6191ab64890Smrg	n = -pColor->spec.TekHVC.H / 360.0;
6201ab64890Smrg	pColor->spec.TekHVC.H += (n + 1) * 360.0;
6211ab64890Smrg	if (pColor->spec.TekHVC.H >= 360.0)
6221ab64890Smrg	    pColor->spec.TekHVC.H -= 360.0;
6231ab64890Smrg    } else if (pColor->spec.TekHVC.H >= 360.0) {
6241ab64890Smrg	n = pColor->spec.TekHVC.H / 360.0;
6251ab64890Smrg	pColor->spec.TekHVC.H -= n * 360.0;
6261ab64890Smrg    }
6271ab64890Smrg    return(1);
6281ab64890Smrg}
629