diff options
author | rjohnson <rjohnson@noemail.net> | 1998-04-01 09:51:45 (GMT) |
---|---|---|
committer | rjohnson <rjohnson@noemail.net> | 1998-04-01 09:51:45 (GMT) |
commit | 9c5b7f2b7e472536ed2e7c915ead05e2aa264182 (patch) | |
tree | 8fb30cb152c4dc191be47fa043d2e6f5ea38c7ba /unix/tkUnixColor.c | |
parent | 1d0efcbe267f2c0eb73869862522fb20fb2d63ca (diff) | |
download | tk-9c5b7f2b7e472536ed2e7c915ead05e2aa264182.zip tk-9c5b7f2b7e472536ed2e7c915ead05e2aa264182.tar.gz tk-9c5b7f2b7e472536ed2e7c915ead05e2aa264182.tar.bz2 |
Initial revision
FossilOrigin-Name: 2bf55ca9aa942b581137b9f474da5ad9c1480de4
Diffstat (limited to 'unix/tkUnixColor.c')
-rw-r--r-- | unix/tkUnixColor.c | 424 |
1 files changed, 424 insertions, 0 deletions
diff --git a/unix/tkUnixColor.c b/unix/tkUnixColor.c new file mode 100644 index 0000000..d3a5a27 --- /dev/null +++ b/unix/tkUnixColor.c @@ -0,0 +1,424 @@ +/* + * tkUnixColor.c -- + * + * This file contains the platform specific color routines + * needed for X support. + * + * Copyright (c) 1996 by Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * SCCS: @(#) tkUnixColor.c 1.1 96/10/22 16:52:31 + */ + +#include <tkColor.h> + +/* + * If a colormap fills up, attempts to allocate new colors from that + * colormap will fail. When that happens, we'll just choose the + * closest color from those that are available in the colormap. + * One of the following structures will be created for each "stressed" + * colormap to keep track of the colors that are available in the + * colormap (otherwise we would have to re-query from the server on + * each allocation, which would be very slow). These entries are + * flushed after a few seconds, since other clients may release or + * reallocate colors over time. + */ + +struct TkStressedCmap { + Colormap colormap; /* X's token for the colormap. */ + int numColors; /* Number of entries currently active + * at *colorPtr. */ + XColor *colorPtr; /* Pointer to malloc'ed array of all + * colors that seem to be available in + * the colormap. Some may not actually + * be available, e.g. because they are + * read-write for another client; when + * we find this out, we remove them + * from the array. */ + struct TkStressedCmap *nextPtr; /* Next in list of all stressed + * colormaps for the display. */ +}; + +/* + * Forward declarations for procedures defined in this file: + */ + +static void DeleteStressedCmap _ANSI_ARGS_((Display *display, + Colormap colormap)); +static void FindClosestColor _ANSI_ARGS_((Tk_Window tkwin, + XColor *desiredColorPtr, XColor *actualColorPtr)); + +/* + *---------------------------------------------------------------------- + * + * TkpFreeColor -- + * + * Release the specified color back to the system. + * + * Results: + * None + * + * Side effects: + * Invalidates the colormap cache for the colormap associated with + * the given color. + * + *---------------------------------------------------------------------- + */ + +void +TkpFreeColor(tkColPtr) + TkColor *tkColPtr; /* Color to be released. Must have been + * allocated by TkpGetColor or + * TkpGetColorByValue. */ +{ + Visual *visual; + Screen *screen = tkColPtr->screen; + + /* + * Careful! Don't free black or white, since this will + * make some servers very unhappy. Also, there is a bug in + * some servers (such Sun's X11/NeWS server) where reference + * counting is performed incorrectly, so that if a color is + * allocated twice in different places and then freed twice, + * the second free generates an error (this bug existed as of + * 10/1/92). To get around this problem, ignore errors that + * occur during the free operation. + */ + + visual = tkColPtr->visual; + if ((visual->class != StaticGray) && (visual->class != StaticColor) + && (tkColPtr->color.pixel != BlackPixelOfScreen(screen)) + && (tkColPtr->color.pixel != WhitePixelOfScreen(screen))) { + Tk_ErrorHandler handler; + + handler = Tk_CreateErrorHandler(DisplayOfScreen(screen), + -1, -1, -1, (Tk_ErrorProc *) NULL, (ClientData) NULL); + XFreeColors(DisplayOfScreen(screen), tkColPtr->colormap, + &tkColPtr->color.pixel, 1, 0L); + Tk_DeleteErrorHandler(handler); + } + DeleteStressedCmap(DisplayOfScreen(screen), tkColPtr->colormap); +} + +/* + *---------------------------------------------------------------------- + * + * TkpGetColor -- + * + * Allocate a new TkColor for the color with the given name. + * + * Results: + * Returns a newly allocated TkColor, or NULL on failure. + * + * Side effects: + * May invalidate the colormap cache associated with tkwin upon + * allocating a new colormap entry. Allocates a new TkColor + * structure. + * + *---------------------------------------------------------------------- + */ + +TkColor * +TkpGetColor(tkwin, name) + Tk_Window tkwin; /* Window in which color will be used. */ + Tk_Uid name; /* Name of color to allocated (in form + * suitable for passing to XParseColor). */ +{ + Display *display = Tk_Display(tkwin); + Colormap colormap = Tk_Colormap(tkwin); + XColor color; + TkColor *tkColPtr; + + /* + * Map from the name to a pixel value. Call XAllocNamedColor rather than + * XParseColor for non-# names: this saves a server round-trip for those + * names. + */ + + if (*name != '#') { + XColor screen; + + if (XAllocNamedColor(display, colormap, name, &screen, + &color) != 0) { + DeleteStressedCmap(display, colormap); + } else { + /* + * Couldn't allocate the color. Try translating the name to + * a color value, to see whether the problem is a bad color + * name or a full colormap. If the colormap is full, then + * pick an approximation to the desired color. + */ + + if (XLookupColor(display, colormap, name, &color, + &screen) == 0) { + return (TkColor *) NULL; + } + FindClosestColor(tkwin, &screen, &color); + } + } else { + if (XParseColor(display, colormap, name, &color) == 0) { + return (TkColor *) NULL; + } + if (XAllocColor(display, colormap, &color) != 0) { + DeleteStressedCmap(display, colormap); + } else { + FindClosestColor(tkwin, &color, &color); + } + } + + tkColPtr = (TkColor *) ckalloc(sizeof(TkColor)); + tkColPtr->color = color; + + return tkColPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TkpGetColorByValue -- + * + * Given a desired set of red-green-blue intensities for a color, + * locate a pixel value to use to draw that color in a given + * window. + * + * Results: + * The return value is a pointer to an TkColor structure that + * indicates the closest red, blue, and green intensities available + * to those specified in colorPtr, and also specifies a pixel + * value to use to draw in that color. + * + * Side effects: + * May invalidate the colormap cache for the specified window. + * Allocates a new TkColor structure. + * + *---------------------------------------------------------------------- + */ + +TkColor * +TkpGetColorByValue(tkwin, colorPtr) + Tk_Window tkwin; /* Window in which color will be used. */ + XColor *colorPtr; /* Red, green, and blue fields indicate + * desired color. */ +{ + Display *display = Tk_Display(tkwin); + Colormap colormap = Tk_Colormap(tkwin); + TkColor *tkColPtr = (TkColor *) ckalloc(sizeof(TkColor)); + + tkColPtr->color.red = colorPtr->red; + tkColPtr->color.green = colorPtr->green; + tkColPtr->color.blue = colorPtr->blue; + if (XAllocColor(display, colormap, &tkColPtr->color) != 0) { + DeleteStressedCmap(display, colormap); + } else { + FindClosestColor(tkwin, &tkColPtr->color, &tkColPtr->color); + } + + return tkColPtr; +} + +/* + *---------------------------------------------------------------------- + * + * FindClosestColor -- + * + * When Tk can't allocate a color because a colormap has filled + * up, this procedure is called to find and allocate the closest + * available color in the colormap. + * + * Results: + * There is no return value, but *actualColorPtr is filled in + * with information about the closest available color in tkwin's + * colormap. This color has been allocated via X, so it must + * be released by the caller when the caller is done with it. + * + * Side effects: + * A color is allocated. + * + *---------------------------------------------------------------------- + */ + +static void +FindClosestColor(tkwin, desiredColorPtr, actualColorPtr) + Tk_Window tkwin; /* Window where color will be used. */ + XColor *desiredColorPtr; /* RGB values of color that was + * wanted (but unavailable). */ + XColor *actualColorPtr; /* Structure to fill in with RGB and + * pixel for closest available + * color. */ +{ + TkStressedCmap *stressPtr; + double tmp, distance, closestDistance; + int i, closest, numFound; + XColor *colorPtr; + TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; + Colormap colormap = Tk_Colormap(tkwin); + XVisualInfo template, *visInfoPtr; + + /* + * Find the TkStressedCmap structure for this colormap, or create + * a new one if needed. + */ + + for (stressPtr = dispPtr->stressPtr; ; stressPtr = stressPtr->nextPtr) { + if (stressPtr == NULL) { + stressPtr = (TkStressedCmap *) ckalloc(sizeof(TkStressedCmap)); + stressPtr->colormap = colormap; + template.visualid = XVisualIDFromVisual(Tk_Visual(tkwin)); + visInfoPtr = XGetVisualInfo(Tk_Display(tkwin), + VisualIDMask, &template, &numFound); + if (numFound < 1) { + panic("FindClosestColor couldn't lookup visual"); + } + stressPtr->numColors = visInfoPtr->colormap_size; + XFree((char *) visInfoPtr); + stressPtr->colorPtr = (XColor *) ckalloc((unsigned) + (stressPtr->numColors * sizeof(XColor))); + for (i = 0; i < stressPtr->numColors; i++) { + stressPtr->colorPtr[i].pixel = (unsigned long) i; + } + XQueryColors(dispPtr->display, colormap, stressPtr->colorPtr, + stressPtr->numColors); + stressPtr->nextPtr = dispPtr->stressPtr; + dispPtr->stressPtr = stressPtr; + break; + } + if (stressPtr->colormap == colormap) { + break; + } + } + + /* + * Find the color that best approximates the desired one, then + * try to allocate that color. If that fails, it must mean that + * the color was read-write (so we can't use it, since it's owner + * might change it) or else it was already freed. Try again, + * over and over again, until something succeeds. + */ + + while (1) { + if (stressPtr->numColors == 0) { + panic("FindClosestColor ran out of colors"); + } + closestDistance = 1e30; + closest = 0; + for (colorPtr = stressPtr->colorPtr, i = 0; i < stressPtr->numColors; + colorPtr++, i++) { + /* + * Use Euclidean distance in RGB space, weighted by Y (of YIQ) + * as the objective function; this accounts for differences + * in the color sensitivity of the eye. + */ + + tmp = .30*(((int) desiredColorPtr->red) - (int) colorPtr->red); + distance = tmp*tmp; + tmp = .61*(((int) desiredColorPtr->green) - (int) colorPtr->green); + distance += tmp*tmp; + tmp = .11*(((int) desiredColorPtr->blue) - (int) colorPtr->blue); + distance += tmp*tmp; + if (distance < closestDistance) { + closest = i; + closestDistance = distance; + } + } + if (XAllocColor(dispPtr->display, colormap, + &stressPtr->colorPtr[closest]) != 0) { + *actualColorPtr = stressPtr->colorPtr[closest]; + return; + } + + /* + * Couldn't allocate the color. Remove it from the table and + * go back to look for the next best color. + */ + + stressPtr->colorPtr[closest] = + stressPtr->colorPtr[stressPtr->numColors-1]; + stressPtr->numColors -= 1; + } +} + +/* + *---------------------------------------------------------------------- + * + * DeleteStressedCmap -- + * + * This procedure releases the information cached for "colormap" + * so that it will be refetched from the X server the next time + * it is needed. + * + * Results: + * None. + * + * Side effects: + * The TkStressedCmap structure for colormap is deleted; the + * colormap is no longer considered to be "stressed". + * + * Note: + * This procedure is invoked whenever a color in a colormap is + * freed, and whenever a color allocation in a colormap succeeds. + * This guarantees that TkStressedCmap structures are always + * deleted before the corresponding Colormap is freed. + * + *---------------------------------------------------------------------- + */ + +static void +DeleteStressedCmap(display, colormap) + Display *display; /* Xlib's handle for the display + * containing the colormap. */ + Colormap colormap; /* Colormap to flush. */ +{ + TkStressedCmap *prevPtr, *stressPtr; + TkDisplay *dispPtr = TkGetDisplay(display); + + for (prevPtr = NULL, stressPtr = dispPtr->stressPtr; stressPtr != NULL; + prevPtr = stressPtr, stressPtr = stressPtr->nextPtr) { + if (stressPtr->colormap == colormap) { + if (prevPtr == NULL) { + dispPtr->stressPtr = stressPtr->nextPtr; + } else { + prevPtr->nextPtr = stressPtr->nextPtr; + } + ckfree((char *) stressPtr->colorPtr); + ckfree((char *) stressPtr); + return; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpCmapStressed -- + * + * Check to see whether a given colormap is known to be out + * of entries. + * + * Results: + * 1 is returned if "colormap" is stressed (i.e. it has run out + * of entries recently), 0 otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TkpCmapStressed(tkwin, colormap) + Tk_Window tkwin; /* Window that identifies the display + * containing the colormap. */ + Colormap colormap; /* Colormap to check for stress. */ +{ + TkStressedCmap *stressPtr; + + for (stressPtr = ((TkWindow *) tkwin)->dispPtr->stressPtr; + stressPtr != NULL; stressPtr = stressPtr->nextPtr) { + if (stressPtr->colormap == colormap) { + return 1; + } + } + return 0; +} |