3 * Code and supporting documentation (c) Copyright 1990 1991 Tektronix, Inc.
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.
14 * Tektronix makes no representation about the suitability of this software
15 * for any purpose. It is provided "as is" and with all faults.
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.
29 * Source for _XcmsLookupColorName().
44 #include <X11/keysymdef.h>
48 static Status LoadColornameDB(void);
53 * #define declarations local to this package.
56 #define XCMSDB "/usr/lib/X11/Xcms.txt"
60 # define isgraph(c) (isprint((c)) && !isspace((c)))
63 #ifndef XCMSDB_MAXLINELEN
64 # define XCMSDB_MAXLINELEN 256
67 #define FORMAT_VERSION "0.1"
68 #define START_TOKEN "XCMS_COLORDB_START"
69 #define END_TOKEN "XCMS_COLORDB_END"
70 #define DELIM_CHAR '\t'
72 #define NOT_VISITED 0x0
75 #define XcmsDbInitNone -1
76 #define XcmsDbInitFailure 0
77 #define XcmsDbInitSuccess 1
82 typedef struct _XcmsPair {
91 static int XcmsColorDbState = XcmsDbInitNone;
94 static XcmsPair *pairs;
95 static const char whitePtStr[] = "WhitePoint";
98 /************************************************************************
102 ************************************************************************/
106 * _XcmsColorSpaceOfString
110 static XcmsColorSpace *
111 _XcmsColorSpaceOfString(
113 const char *color_string)
116 * Returns a pointer to the color space structure
117 * (XcmsColorSpace) associated with the specified color string.
120 * Pointer to matching XcmsColorSpace structure if found;
127 XcmsColorSpace **papColorSpaces;
131 if ((pchar = strchr(color_string, ':')) == NULL) {
134 n = (int)(pchar - color_string);
141 * First try Device-Independent color spaces
143 papColorSpaces = _XcmsDIColorSpaces;
144 if (papColorSpaces != NULL) {
145 while (*papColorSpaces != NULL) {
146 if (strncmp((*papColorSpaces)->prefix, color_string, n) == 0 &&
147 !((*papColorSpaces)->prefix)[n]) {
148 return(*papColorSpaces);
155 * Next try Device-Dependent color spaces
157 papColorSpaces = ((XcmsFunctionSet *)ccc->pPerScrnInfo->functionSet)->DDColorSpaces;
158 if (papColorSpaces != NULL) {
159 while (*papColorSpaces != NULL) {
160 if (strncmp((*papColorSpaces)->prefix, color_string, n) == 0 &&
161 !((*papColorSpaces)->prefix)[n]) {
162 return(*papColorSpaces);
174 * _XcmsParseColorString
179 _XcmsParseColorString(
181 const char *color_string,
185 * Assuming color_string contains a numerical string color
186 * specification, attempts to parse a string into an
187 * XcmsColor structure.
190 * 0 if failed; otherwise non-zero.
193 * A color string containing a numerical color specification
194 * must be in ISO Latin-1 encoding!
197 XcmsColorSpace *pColorSpace;
199 char *string_lowered;
208 * While copying color_string to string_lowered, convert to lowercase
210 if ((len = strlen(color_string)) >= sizeof(string_buf)) {
211 string_lowered = (char *) Xmalloc(len+1);
213 string_lowered = string_buf;
216 _XcmsCopyISOLatin1Lowered(string_lowered, color_string);
218 if (*string_lowered == '#') {
219 if ((pColorSpace = _XcmsColorSpaceOfString(ccc, "rgb:")) != NULL) {
220 res = (*pColorSpace->parseString)(string_lowered, pColor);
221 if (len >= sizeof(string_buf)) Xfree(string_lowered);
226 if ((pColorSpace = _XcmsColorSpaceOfString(ccc, string_lowered)) != NULL) {
227 res = (*pColorSpace->parseString)(string_lowered, pColor);
228 if (len >= sizeof(string_buf)) Xfree(string_lowered);
232 if (len >= sizeof(string_buf)) Xfree(string_lowered);
239 * FirstCmp - Compare color names of pair recs
244 FirstCmp(const void *p1, const void *p2)
247 * Compares the color names of XcmsColorTuples.
248 * This routine is public to allow access from qsort???.
252 * < 0 if first precedes second,
253 * > 0 if first succeeds second.
257 return(strcmp(((XcmsPair *)p1)->first, ((XcmsPair *)p2)->first));
264 * stringSectionSize - determine memory needed for strings
279 XcmsPair *pair = pairs;
281 for (i = 0; i < nEntries; i++, pair++) {
282 if (pair->flag != CYCLE) {
283 pair->flag = NOT_VISITED;
293 * field2 - extract two fields
300 char delim, /* in: field delimiter */
301 char **p1, /* in/out: pointer to pointer to field 1 */
302 char **p2) /* in/out: pointer to pointer to field 2 */
305 * Extracts two fields from a "record".
308 * XcmsSuccess if succeeded, otherwise XcmsFailure.
315 while (!isgraph(*pBuf)) {
316 if ((*pBuf != '\n') || (*pBuf != '\0')) {
319 if (isspace(*pBuf) || (*pBuf == delim)) {
325 /* Find end of Field 2 */
326 while (isprint(*pBuf) && (*pBuf != delim)) {
329 if ((*pBuf == '\n') || (*pBuf == '\0')) {
332 if ((*pBuf == ' ') || (*pBuf == delim)) {
333 *pBuf++ = '\0'; /* stuff end of string character */
339 while (!isgraph(*pBuf)) {
340 if ((*pBuf == '\n') || (*pBuf == '\0')) {
343 if (isspace(*pBuf) || (*pBuf == delim)) {
349 /* Find end of Field 2 */
350 while (isprint(*pBuf) && (*pBuf != delim)) {
354 *pBuf = '\0'; /* stuff end of string character */
363 * _XcmsLookupColorName - Lookup DB entry for a color name
368 _XcmsLookupColorName(
374 * Searches for an entry in the Device-Independent Color Name
375 * Database for the specified string.
378 * XcmsFailure if failed to find a matching entry in
380 * XcmsSuccess if succeeded in converting color name to
382 * _XCMS_NEWNAME if succeeded in converting color string (which
383 * is a color name to yet another color name. Note
384 * that the new name is passed back via 'name'.
388 char name_lowered_64[64];
390 register int i, j, left, right;
393 XcmsPair *pair = NULL;
396 * Check state of Database:
401 if (XcmsColorDbState == XcmsDbInitFailure) {
404 if (XcmsColorDbState == XcmsDbInitNone) {
405 if (!LoadColornameDB()) {
413 * While copying name to name_lowered, convert to lowercase
419 if ((len = strlen(tmpName)) > 63) {
420 name_lowered = (char *) Xmalloc(len+1);
422 name_lowered = name_lowered_64;
425 _XcmsCopyISOLatin1Lowered(name_lowered, tmpName);
428 * Now, remove spaces.
430 for (i = 0, j = 0; j < len; j++) {
431 if (!isspace(name_lowered[j])) {
432 name_lowered[i++] = name_lowered[j];
435 name_lowered[i] = '\0';
438 right = nEntries - 1;
439 while (left <= right) {
440 i = (left + right) >> 1;
442 j = strcmp(name_lowered, pair->first);
451 if (len > 63) Xfree(name_lowered);
455 if (*name != tmpName) {
458 return(_XCMS_NEWNAME);
463 if (pair->flag == CYCLE) {
466 if (pair->flag == VISITED) {
471 if (_XcmsParseColorString(ccc, pair->second, pColor) == XcmsSuccess) {
472 /* f2 contains a numerical string specification */
475 /* f2 does not contain a numerical string specification */
476 tmpName = pair->second;
477 pair->flag = VISITED;
495 * Removes spaces from string.
507 for (i = strlen(pString); i; i--, cptr++) {
508 if (!isspace(*cptr)) {
520 * stringSectionSize - determine memory needed for strings
531 * Determines the amount of memory required to store the
532 * color name strings and also the number of strings.
535 * XcmsSuccess if succeeded, otherwise XcmsFailure.
539 char buf[XCMSDB_MAXLINELEN];
540 char token[XCMSDB_MAXLINELEN];
541 char token2[XCMSDB_MAXLINELEN];
551 * Advance to START_TOKEN
552 * Anything before is just considered as comments.
555 while((pBuf = fgets(buf, XCMSDB_MAXLINELEN, stream)) != NULL) {
556 if ((sscanf(buf, "%s %s", token, token2))
557 && (strcmp(token, START_TOKEN) == 0)) {
558 if (strcmp(token2, FORMAT_VERSION) != 0) {
559 /* text file not in the right format */
563 } /* else it was just a blank line or comment */
570 while((fgets(buf, XCMSDB_MAXLINELEN, stream)) != NULL) {
571 if ((sscanf(buf, "%s", token)) && (strcmp(token, END_TOKEN) == 0)) {
575 if (field2(buf, DELIM_CHAR, &f1, &f2) != XcmsSuccess) {
581 (*pSectionSize) += (i = strlen(f1)) + 1;
582 for (; i; i--, f1++) {
583 /* REMOVE SPACES FROM COUNT */
589 (*pSectionSize) += (i = strlen(f2)) + 1;
590 for (; i; i--, f2++) {
591 /* REMOVE SPACES FROM COUNT */
605 * ReadColornameDB - Read the Color Name Database
616 * Loads the Color Name Database from a text file.
619 * XcmsSuccess if succeeded, otherwise XcmsFailure.
623 char buf[XCMSDB_MAXLINELEN];
624 char token[XCMSDB_MAXLINELEN];
625 char token2[XCMSDB_MAXLINELEN];
631 * Advance to START_TOKEN
632 * Anything before is just considered as comments.
635 while((pBuf = fgets(buf, XCMSDB_MAXLINELEN, stream)) != NULL) {
636 if ((sscanf(buf, "%s %s", token, token2))
637 && (strcmp(token, START_TOKEN) == 0)) {
638 if (strcmp(token2, FORMAT_VERSION) != 0) {
639 /* text file not in the right format */
643 } /* else it was just a blank line or comment */
651 * Process lines between START_TOKEN to END_TOKEN
654 while ((fgets(buf, XCMSDB_MAXLINELEN, stream)) != NULL) {
655 if ((sscanf(buf, "%s", token)) && (strcmp(token, END_TOKEN) == 0)) {
657 * Found END_TOKEN so break out of for loop
665 if (field2(buf, DELIM_CHAR, &f1, &f2) != XcmsSuccess) {
675 pRec->first = pString;
676 _XcmsCopyISOLatin1Lowered(pString, f1);
677 pString += (1 + RemoveSpaces(pString));
678 pRec->second = pString;
680 _XcmsCopyISOLatin1Lowered(pString, f2);
681 pString += RemoveSpaces(pString) + 1;
692 * LoadColornameDB - Load the Color Name Database
697 LoadColornameDB(void)
700 * Loads the Color Name Database from a text file.
703 * XcmsSuccess if succeeded, otherwise XcmsFailure.
709 const char *pathname;
713 /* use and name of this env var is not part of the standard */
714 /* implementation-dependent feature */
715 if ((pathname = getenv("XCMSDB")) == NULL) {
719 pathname = __XOS2RedirRoot(pathname);
722 length = strlen(pathname);
723 if ((length == 0) || (length >= (BUFSIZ - 5))){
724 XcmsColorDbState = XcmsDbInitFailure;
728 if (stat(pathname, &txt)) {
729 /* can't stat file */
730 XcmsColorDbState = XcmsDbInitFailure;
734 if ((stream = _XFopenFile (pathname, "r")) == NULL) {
735 /* can't open file */
736 XcmsColorDbState = XcmsDbInitFailure;
740 if (stringSectionSize(stream, &nEntries, &size) != XcmsSuccess ||
742 (void) fclose(stream);
743 XcmsColorDbState = XcmsDbInitFailure;
748 strings = (char *) Xmalloc(size);
749 pairs = (XcmsPair *)Xcalloc(nEntries, sizeof(XcmsPair));
751 ReadColornameDB(stream, pairs, strings);
752 (void) fclose(stream);
757 qsort((char *)pairs, nEntries, sizeof(XcmsPair), FirstCmp);
759 XcmsColorDbState = XcmsDbInitSuccess;
764 /************************************************************************
766 * API PRIVATE ROUTINES *
768 ************************************************************************/
772 * _XcmsCopyISOLatin1Lowered
777 _XcmsCopyISOLatin1Lowered(
782 * ISO Latin-1 case conversion routine
783 * Identical to XmuCopyISOLatin1Lowered() but provided here
784 * to eliminate need to link with libXmu.a.
787 * This routine is also used in XcmsFormatOfPrefix.
794 register unsigned char *dest;
795 register const unsigned char *source;
797 for (dest = (unsigned char *)dst, source = (const unsigned char *)src;
801 if ((*source >= XK_A) && (*source <= XK_Z))
802 *dest = *source + (XK_a - XK_A);
803 else if ((*source >= XK_Agrave) && (*source <= XK_Odiaeresis))
804 *dest = *source + (XK_agrave - XK_Agrave);
805 else if ((*source >= XK_Ooblique) && (*source <= XK_Thorn))
806 *dest = *source + (XK_oslash - XK_Ooblique);
816 * _XcmsResolveColorString -
821 _XcmsResolveColorString (
823 const char **color_string,
824 XcmsColor *pColor_exact_return,
825 XcmsColorFormat result_format)
828 * The XcmsLookupColor function finds the color specification
829 * associated with a color name in the Device-Independent Color
832 * XcmsFailure if failed to convert valid color string.
833 * XcmsSuccess if succeeded in converting color string to
835 * _XCMS_NEWNAME if failed to parse the string or find it in
836 * the database, or if succeeded in looking it up and
837 * found another name which is not in the database.
838 * Note that the new name is returned in color_string.
840 * This function returns both the color specification found in the
841 * database (db specification) and the color specification for the
842 * color displayable by the specified screen (screen
843 * specification). The calling routine sets the format for these
844 * returned specifications in the XcmsColor format component.
845 * If XcmsUndefinedFormat, the specification is returned in the
846 * format used to store the color in the database.
849 XcmsColor dbWhitePt; /* whitePt associated with pColor_exact_return*/
850 /* the screen's white point */
851 XcmsColor *pClientWhitePt;
853 const char *strptr = whitePtStr;
856 * 0. Check for invalid arguments.
858 if (ccc == NULL || (*color_string)[0] == '\0' || pColor_exact_return == NULL) {
863 * 1. First attempt to parse the string
864 * If successful, then convert the specification to the target format
867 if (_XcmsParseColorString(ccc, *color_string, pColor_exact_return)
869 if (result_format != XcmsUndefinedFormat
870 && pColor_exact_return->format != result_format) {
871 /* need to be converted to the target format */
872 return(XcmsConvertColors(ccc, pColor_exact_return, 1,
873 result_format, (Bool *)NULL));
880 * 2. Attempt to find it in the DI Color Name Database
884 * a. Convert String into a XcmsColor structure
885 * Attempt to extract the specification for color_string from the
886 * DI Database (pColor_exact_return). If the DI Database does not
887 * have this entry, then return failure.
889 retval = _XcmsLookupColorName(ccc, color_string, pColor_exact_return);
891 if (retval != XcmsSuccess) {
892 /* color_string replaced with a color name, or not found */
893 return(_XCMS_NEWNAME);
896 if (pColor_exact_return->format == XcmsUndefinedFormat) {
901 * b. If result_format not defined, then assume target format
902 * is the exact format.
904 if (result_format == XcmsUndefinedFormat) {
905 result_format = pColor_exact_return->format;
908 if ((ClientWhitePointOfCCC(ccc))->format == XcmsUndefinedFormat) {
909 pClientWhitePt = ScreenWhitePointOfCCC(ccc);
911 pClientWhitePt = ClientWhitePointOfCCC(ccc);
915 * c. Convert to the target format, making adjustments for white
916 * point differences as necessary.
918 if (XCMS_DD_ID(pColor_exact_return->format)) {
920 * The spec format is Device-Dependent, therefore assume the
921 * its white point is the Screen White Point.
923 if (XCMS_DD_ID(result_format)) {
925 * Target format is Device-Dependent
926 * Therefore, DD --> DD conversion
928 return(_XcmsDDConvertColors(ccc, pColor_exact_return,
929 1, result_format, (Bool *) NULL));
932 * Target format is Device-Independent
933 * Therefore, DD --> DI conversion
935 if (ccc->whitePtAdjProc && !_XcmsEqualWhitePts(ccc,
936 pClientWhitePt, ScreenWhitePointOfCCC(ccc))) {
937 return((*ccc->whitePtAdjProc)(ccc, ScreenWhitePointOfCCC(ccc),
938 pClientWhitePt, result_format,
939 pColor_exact_return, 1, (Bool *) NULL));
941 if (_XcmsDDConvertColors(ccc, pColor_exact_return, 1,
942 XcmsCIEXYZFormat, (Bool *) NULL) == XcmsFailure) {
945 return(_XcmsDIConvertColors(ccc, pColor_exact_return,
946 pClientWhitePt, 1, result_format));
951 * The spec format is Device-Independent, therefore attempt
952 * to find a database white point.
954 * If the Database does not have a white point, then assume the
955 * database white point is the same as the Screen White Point.
958 if (_XcmsLookupColorName(ccc, &strptr, &dbWhitePt) != 1) {
959 memcpy((char *)&dbWhitePt,
960 (char *)&ccc->pPerScrnInfo->screenWhitePt,
963 if (XCMS_DD_ID(result_format)) {
965 * Target format is Device-Dependent
966 * Therefore, DI --> DD conversion
968 if (ccc->whitePtAdjProc && !_XcmsEqualWhitePts(ccc,
969 &dbWhitePt, ScreenWhitePointOfCCC(ccc))) {
970 return((*ccc->whitePtAdjProc)(ccc, &dbWhitePt,
971 ScreenWhitePointOfCCC(ccc), result_format,
972 pColor_exact_return, 1, (Bool *)NULL));
974 if (pColor_exact_return->format != XcmsCIEXYZFormat) {
975 if (_XcmsDIConvertColors(ccc, pColor_exact_return,
976 &dbWhitePt, 1, XcmsCIEXYZFormat) == XcmsFailure) {
980 return (_XcmsDDConvertColors(ccc, pColor_exact_return, 1,
981 result_format, (Bool *)NULL));
985 * Target format is Device-Independent
986 * Therefore, DI --> DI conversion
988 if (ccc->whitePtAdjProc && !_XcmsEqualWhitePts(ccc,
989 &dbWhitePt, pClientWhitePt)) {
991 * The calling routine wants to resolve this color
992 * in terms if it's white point (i.e. Client White Point).
993 * Therefore, apply white adjustment for the displacement
994 * between dbWhitePt to clientWhitePt.
996 return((*ccc->whitePtAdjProc)(ccc, &dbWhitePt,
997 pClientWhitePt, result_format,
998 pColor_exact_return, 1, (Bool *)NULL));
999 } else if (_XcmsEqualWhitePts(ccc,
1000 &dbWhitePt, pClientWhitePt)) {
1002 * Can use either dbWhitePt or pClientWhitePt to
1003 * convert to the result_format.
1005 if (pColor_exact_return->format == result_format) {
1006 return(XcmsSuccess);
1008 return (_XcmsDIConvertColors(ccc, pColor_exact_return,
1009 &dbWhitePt, 1, result_format));
1013 * Need to convert to a white point independent color
1014 * space (let's choose CIEXYZ) then convert to the
1015 * target color space. Why? Lets assume that
1016 * pColor_exact_return->format and result format
1017 * are white point dependent format (e.g., CIELUV, CIELAB,
1018 * TekHVC ... same or any combination). If so, we'll
1019 * need to convert the color with dbWhitePt to an absolute
1020 * spec (i.e. non-white point dependent) then convert that
1021 * absolute value with clientWhitePt to the result_format.
1023 if (pColor_exact_return->format != XcmsCIEXYZFormat) {
1024 if (_XcmsDIConvertColors(ccc, pColor_exact_return,
1025 &dbWhitePt, 1, XcmsCIEXYZFormat) == XcmsFailure) {
1026 return(XcmsFailure);
1029 if (result_format == XcmsCIEXYZFormat) {
1030 return(XcmsSuccess);
1032 return(_XcmsDIConvertColors(ccc, pColor_exact_return,
1033 pClientWhitePt, 1, result_format));