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