/* * tkWinColor.c -- * * Functions to map color names to system color values. * * Copyright (c) 1995 Sun Microsystems, Inc. * Copyright (c) 1994 Software Research Associates, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * SCCS: @(#) tkWinColor.c 1.12 96/07/30 18:52:26 */ #include "tkWinInt.h" #include "xcolors.h" /* * This variable indicates whether the color table has been initialized. */ static int initialized = 0; /* * colorTable is a hash table used to look up X colors by name. */ static Tcl_HashTable colorTable; /* * The SystemColorEntries array contains the names and index values for the * Windows indirect system color names. */ typedef struct { char *name; int index; } SystemColorEntry; static SystemColorEntry sysColorEntries[] = { "SystemActiveBorder", COLOR_ACTIVEBORDER, "SystemActiveCaption", COLOR_ACTIVECAPTION, "SystemAppWorkspace", COLOR_APPWORKSPACE, "SystemBackground", COLOR_BACKGROUND, "SystemButtonFace", COLOR_BTNFACE, "SystemButtonHighlight", COLOR_BTNHIGHLIGHT, "SystemButtonShadow", COLOR_BTNSHADOW, "SystemButtonText", COLOR_BTNTEXT, "SystemCaptionText", COLOR_CAPTIONTEXT, "SystemDisabledText", COLOR_GRAYTEXT, "SystemHighlight", COLOR_HIGHLIGHT, "SystemHighlightText", COLOR_HIGHLIGHTTEXT, "SystemInactiveBorder", COLOR_INACTIVEBORDER, "SystemInactiveCaption", COLOR_INACTIVECAPTION, "SystemInactiveCaptionText", COLOR_INACTIVECAPTIONTEXT, "SystemMenu", COLOR_MENU, "SystemMenuText", COLOR_MENUTEXT, "SystemScrollbar", COLOR_SCROLLBAR, "SystemWindow", COLOR_WINDOW, "SystemWindowFrame", COLOR_WINDOWFRAME, "SystemWindowText", COLOR_WINDOWTEXT, NULL, 0 }; /* * The sysColors array is initialized by SetSystemColors(). */ static XColorEntry sysColors[] = { 0, 0, 0, "SystemActiveBorder", 0, 0, 0, "SystemActiveCaption", 0, 0, 0, "SystemAppWorkspace", 0, 0, 0, "SystemBackground", 0, 0, 0, "SystemButtonFace", 0, 0, 0, "SystemButtonHighlight", 0, 0, 0, "SystemButtonShadow", 0, 0, 0, "SystemButtonText", 0, 0, 0, "SystemCaptionText", 0, 0, 0, "SystemDisabledText", 0, 0, 0, "SystemHighlight", 0, 0, 0, "SystemHighlightText", 0, 0, 0, "SystemInactiveBorder", 0, 0, 0, "SystemInactiveCaption", 0, 0, 0, "SystemInactiveCaptionText", 0, 0, 0, "SystemMenu", 0, 0, 0, "SystemMenuText", 0, 0, 0, "SystemScrollbar", 0, 0, 0, "SystemWindow", 0, 0, 0, "SystemWindowFrame", 0, 0, 0, "SystemWindowText", 0, 0, 0, NULL }; /* * Forward declarations for functions defined later in this file. */ static int GetColorByName _ANSI_ARGS_((char *name, XColor *color)); static int GetColorByValue _ANSI_ARGS_((char *value, XColor *color)); static void InitColorTable _ANSI_ARGS_((void)); static void SetSystemColors _ANSI_ARGS_((void)); /* *---------------------------------------------------------------------- * * SetSystemColors -- * * Initializes the sysColors array with the current values for * the system colors. * * Results: * None. * * Side effects: * Changes the RGB values stored in the sysColors array. * *---------------------------------------------------------------------- */ static void SetSystemColors() { SystemColorEntry *sPtr; XColorEntry *ePtr; COLORREF color; for (ePtr = sysColors, sPtr = sysColorEntries; sPtr->name != NULL; ePtr++, sPtr++) { color = GetSysColor(sPtr->index); ePtr->red = GetRValue(color); ePtr->green = GetGValue(color); ePtr->blue = GetBValue(color); } } /* *---------------------------------------------------------------------- * * InitColorTable -- * * Initialize color name database. * * Results: * None. * * Side effects: * Builds a hash table of color names and RGB values. * *---------------------------------------------------------------------- */ static void InitColorTable() { XColorEntry *colorPtr; Tcl_HashEntry *hPtr; int dummy; Tcl_InitHashTable(&colorTable, TCL_STRING_KEYS); /* * Add X colors to table. */ for (colorPtr = xColors; colorPtr->name != NULL; colorPtr++) { hPtr = Tcl_CreateHashEntry(&colorTable, strlwr(colorPtr->name), &dummy); Tcl_SetHashValue(hPtr, colorPtr); } /* * Add Windows indirect system colors to table. */ SetSystemColors(); for (colorPtr = sysColors; colorPtr->name != NULL; colorPtr++) { hPtr = Tcl_CreateHashEntry(&colorTable, strlwr(colorPtr->name), &dummy); Tcl_SetHashValue(hPtr, colorPtr); } initialized = 1; } /* *---------------------------------------------------------------------- * * GetColorByName -- * * Looks for a color in the color table by name, then finds the * closest available color in the palette and converts it to an * XColor structure. * * Results: * If it finds a match, the color is returned in the color * parameter and the return value is 1. Otherwise the return * value is 0. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int GetColorByName(name, color) char *name; /* An X color name, e.g. "red" */ XColor *color; /* The closest available color. */ { Tcl_HashEntry *hPtr; XColorEntry *colorPtr; if (!initialized) { InitColorTable(); } hPtr = Tcl_FindHashEntry(&colorTable, (char *) strlwr(name)); if (hPtr == NULL) { return 0; } colorPtr = (XColorEntry *) Tcl_GetHashValue(hPtr); color->pixel = PALETTERGB(colorPtr->red, colorPtr->green, colorPtr->blue); color->red = colorPtr->red << 8; color->green = colorPtr->green << 8; color->blue = colorPtr->blue << 8; color->flags = DoRed|DoGreen|DoBlue; color->pad = 0; return 1; } /* *---------------------------------------------------------------------- * * GetColorByValue -- * * Parses an X RGB color string and finds the closest available * color in the palette and converts it to an XColor structure. * The returned color will have RGB values in the range 0 to 255. * * Results: * If it finds a match, the color is returned in the color * parameter and the return value is 1. Otherwise the return * value is 0. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int GetColorByValue(value, color) char *value; /* a string of the form "#RGB", "#RRGGBB", */ /* "#RRRGGGBBB", or "#RRRRGGGGBBBB" */ XColor *color; /* The closest available color. */ { char fmt[16]; int i; i = strlen(value+1); if (i % 3) { return 0; } i /= 3; if (i == 0) { return 0; } sprintf(fmt, "%%%dx%%%dx%%%dx", i, i, i); sscanf(value+1, fmt, &color->red, &color->green, &color->blue); /* * Scale the parse values into 8 bits. */ if (i == 1) { color->red <<= 4; color->green <<= 4; color->blue <<= 4; } else if (i != 2) { color->red >>= (4*(i-2)); color->green >>= (4*(i-2)); color->blue >>= (4*(i-2)); } color->pad = 0; color->pixel = PALETTERGB(color->red, color->green, color->blue); color->red = GetRValue(color->pixel) << 8; color->green = GetGValue(color->pixel) << 8; color->blue = GetBValue(color->pixel) << 8; return 1; } /* *---------------------------------------------------------------------- * * XParseColor -- * * Decodes an X color specification. * * Results: * Sets exact_def_return to the parsed color. * * Side effects: * None. * *---------------------------------------------------------------------- */ int XParseColor(display, colormap, spec, exact_def_return) Display* display; Colormap colormap; _Xconst char* spec; XColor* exact_def_return; { /* * Note that we are violating the const-ness of spec. This is * probably OK in most cases. But this is a bug in general. */ if (spec[0] == '#') { return GetColorByValue((char *) spec, exact_def_return); } else { return GetColorByName((char *) spec, exact_def_return); } } /* *---------------------------------------------------------------------- * * XAllocColor -- * * Find the closest available color to the specified XColor. * * Results: * Updates the color argument and returns 1 on success. Otherwise * returns 0. * * Side effects: * Allocates a new color in the palette. * *---------------------------------------------------------------------- */ int XAllocColor(display, colormap, color) Display* display; Colormap colormap; XColor* color; { TkWinColormap *cmap = (TkWinColormap *) colormap; PALETTEENTRY entry, closeEntry; HDC dc = GetDC(NULL); entry.peRed = (color->red) >> 8; entry.peGreen = (color->green) >> 8; entry.peBlue = (color->blue) >> 8; entry.peFlags = 0; if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE) { unsigned long sizePalette = GetDeviceCaps(dc, SIZEPALETTE); UINT newPixel, closePixel; int new, refCount; Tcl_HashEntry *entryPtr; /* * Find the nearest existing palette entry. */ newPixel = RGB(entry.peRed, entry.peGreen, entry.peBlue); closePixel = GetNearestPaletteIndex(cmap->palette, newPixel); GetPaletteEntries(cmap->palette, closePixel, 1, &closeEntry); closePixel = RGB(closeEntry.peRed, closeEntry.peGreen, closeEntry.peBlue); /* * If this is not a duplicate, allocate a new entry. */ if (newPixel != closePixel) { /* * Fails if the palette is full. */ if (cmap->size == sizePalette) { return 0; } cmap->size++; ResizePalette(cmap->palette, cmap->size); SetPaletteEntries(cmap->palette, cmap->size - 1, 1, &entry); } color->pixel = PALETTERGB(entry.peRed, entry.peGreen, entry.peBlue); entryPtr = Tcl_CreateHashEntry(&cmap->refCounts, (char *) color->pixel, &new); if (new) { refCount = 1; } else { refCount = ((int) Tcl_GetHashValue(entryPtr)) + 1; } Tcl_SetHashValue(entryPtr, (ClientData)refCount); } else { /* * Determine what color will actually be used on non-colormap systems. */ color->pixel = GetNearestColor(dc, RGB(entry.peRed, entry.peGreen, entry.peBlue)); color->red = (GetRValue(color->pixel) << 8); color->green = (GetGValue(color->pixel) << 8); color->blue = (GetBValue(color->pixel) << 8); } ReleaseDC(NULL, dc); return 1; } /* *---------------------------------------------------------------------- * * XAllocNamedColor -- * * Find the closest color of the given name. * * Results: * Returns 1 on success with the resulting color in * exact_def_return. Returns 0 on failure. * * Side effects: * Allocates a new color in the palette. * *---------------------------------------------------------------------- */ int XAllocNamedColor(display, colormap, color_name, screen_def_return, exact_def_return) Display* display; Colormap colormap; _Xconst char* color_name; XColor* screen_def_return; XColor* exact_def_return; { int rval = GetColorByName((char *) color_name, exact_def_return); if (rval) { *screen_def_return = *exact_def_return; return XAllocColor(display, colormap, exact_def_return); } return 0; } /* *---------------------------------------------------------------------- * * XFreeColors -- * * Deallocate a block of colors. * * Results: * None. * * Side effects: * Removes entries for the current palette and compacts the * remaining set. * *---------------------------------------------------------------------- */ void XFreeColors(display, colormap, pixels, npixels, planes) Display* display; Colormap colormap; unsigned long* pixels; int npixels; unsigned long planes; { TkWinColormap *cmap = (TkWinColormap *) colormap; COLORREF cref; UINT count, index, refCount; int i; PALETTEENTRY entry, *entries; Tcl_HashEntry *entryPtr; HDC dc = GetDC(NULL); /* * We don't have to do anything for non-palette devices. */ if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE) { /* * This is really slow for large values of npixels. */ for (i = 0; i < npixels; i++) { entryPtr = Tcl_FindHashEntry(&cmap->refCounts, (char *) pixels[i]); if (!entryPtr) { panic("Tried to free a color that isn't allocated."); } refCount = (int) Tcl_GetHashValue(entryPtr) - 1; if (refCount == 0) { cref = pixels[i] & 0x00ffffff; index = GetNearestPaletteIndex(cmap->palette, cref); GetPaletteEntries(cmap->palette, index, 1, &entry); if (cref == RGB(entry.peRed, entry.peGreen, entry.peBlue)) { count = cmap->size - index; entries = (PALETTEENTRY *) ckalloc(sizeof(PALETTEENTRY) * count); GetPaletteEntries(cmap->palette, index+1, count, entries); SetPaletteEntries(cmap->palette, index, count, entries); ckfree((char *) entries); cmap->size--; } else { panic("Tried to free a color that isn't allocated."); } Tcl_DeleteHashEntry(entryPtr); } } } ReleaseDC(NULL, dc); } /* *---------------------------------------------------------------------- * * XCreateColormap -- * * Allocate a new colormap. * * Results: * Returns a newly allocated colormap. * * Side effects: * Allocates an empty palette and color list. * *---------------------------------------------------------------------- */ Colormap XCreateColormap(display, w, visual, alloc) Display* display; Window w; Visual* visual; int alloc; { LOGPALETTE logPalette; TkWinColormap *cmap = (TkWinColormap *) ckalloc(sizeof(TkWinColormap)); logPalette.palVersion = 0x300; logPalette.palNumEntries = 1; logPalette.palPalEntry[0].peRed = 0; logPalette.palPalEntry[0].peGreen = 0; logPalette.palPalEntry[0].peBlue = 0; logPalette.palPalEntry[0].peFlags = 0; cmap->palette = CreatePalette(&logPalette); cmap->size = 0; cmap->stale = 0; Tcl_InitHashTable(&cmap->refCounts, TCL_ONE_WORD_KEYS); return (Colormap)cmap; } /* *---------------------------------------------------------------------- * * XFreeColormap -- * * Frees the resources associated with the given colormap. * * Results: * None. * * Side effects: * Deletes the palette associated with the colormap. Note that * the palette must not be selected into a device context when * this occurs. * *---------------------------------------------------------------------- */ void XFreeColormap(display, colormap) Display* display; Colormap colormap; { TkWinColormap *cmap = (TkWinColormap *) colormap; if (!DeleteObject(cmap->palette)) { panic("Unable to free colormap, palette is still selected."); } Tcl_DeleteHashTable(&cmap->refCounts); ckfree((char *) cmap); } /* *---------------------------------------------------------------------- * * TkWinSelectPalette -- * * This function sets up the specified device context with a * given palette. If the palette is stale, it realizes it in * the background unless the palette is the current global * palette. * * Results: * Returns the previous palette selected into the device context. * * Side effects: * May change the system palette. * *---------------------------------------------------------------------- */ HPALETTE TkWinSelectPalette(dc, colormap) HDC dc; Colormap colormap; { TkWinColormap *cmap = (TkWinColormap *) colormap; HPALETTE oldPalette; oldPalette = SelectPalette(dc, cmap->palette, (cmap->palette == TkWinGetSystemPalette()) ? FALSE : TRUE); RealizePalette(dc); return oldPalette; }