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