summaryrefslogtreecommitdiffstats
path: root/tk8.6/unix/tkUnixColor.c
diff options
context:
space:
mode:
Diffstat (limited to 'tk8.6/unix/tkUnixColor.c')
-rw-r--r--tk8.6/unix/tkUnixColor.c450
1 files changed, 450 insertions, 0 deletions
diff --git a/tk8.6/unix/tkUnixColor.c b/tk8.6/unix/tkUnixColor.c
new file mode 100644
index 0000000..43500ad
--- /dev/null
+++ b/tk8.6/unix/tkUnixColor.c
@@ -0,0 +1,450 @@
+/*
+ * 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.
+ */
+
+#include "tkUnixInt.h"
+#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 functions defined in this file:
+ */
+
+static void DeleteStressedCmap(Display *display,
+ Colormap colormap);
+static void FindClosestColor(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(
+ 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, NULL, 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(
+ 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 (((*name - 'A') & 0xdf) < sizeof(tkWebColors)/sizeof(tkWebColors[0])) {
+ if (!((name[0] - 'G') & 0xdf) && !((name[1] - 'R') & 0xdf)
+ && !((name[2] - 'A') & 0xdb) && !((name[3] - 'Y') & 0xdf)
+ && !name[4]) {
+ name = "#808080808080";
+ goto gotWebColor;
+ } else {
+ const char *p = tkWebColors[((*name - 'A') & 0x1f)];
+ if (p) {
+ const char *q = name;
+ while (!((*p - *(++q)) & 0xdf)) {
+ if (!*p++) {
+ name = p;
+ goto gotWebColor;
+ }
+ }
+ }
+ }
+ }
+ if (strlen(name) > 99) {
+ /* Don't bother to parse this. [Bug 2809525]*/
+ return (TkColor *) NULL;
+ } else 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 NULL;
+ }
+ FindClosestColor(tkwin, &screen, &color);
+ }
+ } else {
+ gotWebColor:
+ if (TkParseColor(display, colormap, name, &color) == 0) {
+ return NULL;
+ }
+ if (XAllocColor(display, colormap, &color) != 0) {
+ DeleteStressedCmap(display, colormap);
+ } else {
+ FindClosestColor(tkwin, &color, &color);
+ }
+ }
+
+ tkColPtr = 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(
+ 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 = 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
+ * function 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(
+ 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 = ckalloc(sizeof(TkStressedCmap));
+ stressPtr->colormap = colormap;
+ template.visualid = XVisualIDFromVisual(Tk_Visual(tkwin));
+
+ visInfoPtr = XGetVisualInfo(Tk_Display(tkwin),
+ VisualIDMask, &template, &numFound);
+ if (numFound < 1) {
+ Tcl_Panic("FindClosestColor couldn't lookup visual");
+ }
+
+ stressPtr->numColors = visInfoPtr->colormap_size;
+ XFree((char *) visInfoPtr);
+ stressPtr->colorPtr =
+ ckalloc(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) {
+ Tcl_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 function 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 function 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 *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(stressPtr->colorPtr);
+ ckfree(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(
+ 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;
+}
+
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */