1 /* GDK - The GIMP Drawing Kit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
32 static gint gdk_colormap_match_color (GdkColormap *cmap,
34 const gchar *available);
35 static void gdk_colormap_add (GdkColormap *cmap);
36 static void gdk_colormap_remove (GdkColormap *cmap);
37 static guint gdk_colormap_hash (Colormap *cmap);
38 static gint gdk_colormap_cmp (Colormap *a,
41 static GHashTable *colormap_hash = NULL;
45 gdk_colormap_new (GdkVisual *visual,
48 GdkColormap *colormap;
49 GdkColormapPrivateX *private;
54 g_return_val_if_fail (visual != NULL, NULL);
56 private = g_new (GdkColormapPrivateX, 1);
57 colormap = (GdkColormap*) private;
59 private->xdisplay = gdk_display;
60 private->base.visual = visual;
61 private->base.ref_count = 1;
64 private->last_sync_time = 0;
67 xvisual = ((GdkVisualPrivate*) visual)->xvisual;
69 colormap->size = visual->colormap_size;
70 colormap->colors = NULL;
74 case GDK_VISUAL_GRAYSCALE:
75 case GDK_VISUAL_PSEUDO_COLOR:
76 private->info = g_new0 (GdkColorInfo, colormap->size);
77 colormap->colors = g_new (GdkColor, colormap->size);
79 private->hash = g_hash_table_new ((GHashFunc) gdk_color_hash,
80 (GCompareFunc) gdk_color_equal);
82 private->private_val = private_cmap;
83 private->xcolormap = XCreateColormap (private->xdisplay, gdk_root_window,
84 xvisual, (private_cmap) ? (AllocAll) : (AllocNone));
88 XColor *default_colors;
90 default_colors = g_new (XColor, colormap->size);
92 for (i = 0; i < colormap->size; i++)
93 default_colors[i].pixel = i;
95 XQueryColors (private->xdisplay,
96 DefaultColormap (private->xdisplay, gdk_screen),
97 default_colors, colormap->size);
99 for (i = 0; i < colormap->size; i++)
101 colormap->colors[i].pixel = default_colors[i].pixel;
102 colormap->colors[i].red = default_colors[i].red;
103 colormap->colors[i].green = default_colors[i].green;
104 colormap->colors[i].blue = default_colors[i].blue;
107 gdk_colormap_change (colormap, colormap->size);
109 g_free (default_colors);
113 case GDK_VISUAL_DIRECT_COLOR:
114 private->private_val = TRUE;
115 private->xcolormap = XCreateColormap (private->xdisplay, gdk_root_window,
117 colormap->colors = g_new (GdkColor, colormap->size);
119 size = 1 << visual->red_prec;
120 for (i = 0; i < size; i++)
121 colormap->colors[i].red = i * 65535 / (size - 1);
123 size = 1 << visual->green_prec;
124 for (i = 0; i < size; i++)
125 colormap->colors[i].green = i * 65535 / (size - 1);
127 size = 1 << visual->blue_prec;
128 for (i = 0; i < size; i++)
129 colormap->colors[i].blue = i * 65535 / (size - 1);
131 gdk_colormap_change (colormap, colormap->size);
134 case GDK_VISUAL_STATIC_GRAY:
135 case GDK_VISUAL_STATIC_COLOR:
136 case GDK_VISUAL_TRUE_COLOR:
137 private->private_val = FALSE;
138 private->xcolormap = XCreateColormap (private->xdisplay, gdk_root_window,
143 gdk_colormap_add (colormap);
149 _gdk_colormap_real_destroy (GdkColormap *colormap)
151 GdkColormapPrivateX *private = (GdkColormapPrivateX*) colormap;
153 gdk_colormap_remove (colormap);
154 XFreeColormap (private->xdisplay, private->xcolormap);
157 g_hash_table_destroy (private->hash);
159 g_free (private->info);
160 g_free (colormap->colors);
164 #define MIN_SYNC_TIME 2
167 gdk_colormap_sync (GdkColormap *colormap,
171 GdkColormapPrivateX *private = (GdkColormapPrivateX *)colormap;
176 g_return_if_fail (colormap != NULL);
178 current_time = time (NULL);
179 if (!force && ((current_time - private->last_sync_time) < MIN_SYNC_TIME))
182 private->last_sync_time = current_time;
185 xpalette = g_new (XColor, colormap->size);
187 for (i = 0; i < colormap->size; i++)
189 if (private->info[i].ref_count == 0)
191 xpalette[nlookup].pixel = i;
192 xpalette[nlookup].red = 0;
193 xpalette[nlookup].green = 0;
194 xpalette[nlookup].blue = 0;
199 XQueryColors (gdk_display, private->xcolormap, xpalette, nlookup);
201 for (i = 0; i < nlookup; i++)
203 gulong pixel = xpalette[i].pixel;
204 colormap->colors[pixel].pixel = pixel;
205 colormap->colors[pixel].red = xpalette[i].red;
206 colormap->colors[pixel].green = xpalette[i].green;
207 colormap->colors[pixel].blue = xpalette[i].blue;
215 gdk_colormap_get_system (void)
217 static GdkColormap *colormap = NULL;
218 GdkColormapPrivateX *private;
222 private = g_new (GdkColormapPrivateX, 1);
223 colormap = (GdkColormap*) private;
225 private->xdisplay = gdk_display;
226 private->xcolormap = DefaultColormap (gdk_display, gdk_screen);
227 private->base.visual = gdk_visual_get_system ();
228 private->private_val = FALSE;
229 private->base.ref_count = 1;
231 private->hash = NULL;
232 private->last_sync_time = 0;
233 private->info = NULL;
235 colormap->colors = NULL;
236 colormap->size = private->base.visual->colormap_size;
238 if ((private->base.visual->type == GDK_VISUAL_GRAYSCALE) ||
239 (private->base.visual->type == GDK_VISUAL_PSEUDO_COLOR))
241 private->info = g_new0 (GdkColorInfo, colormap->size);
242 colormap->colors = g_new (GdkColor, colormap->size);
244 private->hash = g_hash_table_new ((GHashFunc) gdk_color_hash,
245 (GCompareFunc) gdk_color_equal);
247 gdk_colormap_sync (colormap, TRUE);
250 gdk_colormap_add (colormap);
257 gdk_colormap_get_system_size (void)
259 return DisplayCells (gdk_display, gdk_screen);
263 gdk_colormap_change (GdkColormap *colormap,
266 GdkColormapPrivateX *private;
274 g_return_if_fail (colormap != NULL);
276 palette = g_new (XColor, ncolors);
278 private = (GdkColormapPrivateX*) colormap;
279 switch (private->base.visual->type)
281 case GDK_VISUAL_GRAYSCALE:
282 case GDK_VISUAL_PSEUDO_COLOR:
283 for (i = 0; i < ncolors; i++)
285 palette[i].pixel = colormap->colors[i].pixel;
286 palette[i].red = colormap->colors[i].red;
287 palette[i].green = colormap->colors[i].green;
288 palette[i].blue = colormap->colors[i].blue;
289 palette[i].flags = DoRed | DoGreen | DoBlue;
292 XStoreColors (private->xdisplay, private->xcolormap, palette, ncolors);
295 case GDK_VISUAL_DIRECT_COLOR:
296 visual = private->base.visual;
298 shift = visual->red_shift;
299 max_colors = 1 << visual->red_prec;
300 size = (ncolors < max_colors) ? (ncolors) : (max_colors);
302 for (i = 0; i < size; i++)
304 palette[i].pixel = i << shift;
305 palette[i].red = colormap->colors[i].red;
306 palette[i].flags = DoRed;
309 XStoreColors (private->xdisplay, private->xcolormap, palette, size);
311 shift = visual->green_shift;
312 max_colors = 1 << visual->green_prec;
313 size = (ncolors < max_colors) ? (ncolors) : (max_colors);
315 for (i = 0; i < size; i++)
317 palette[i].pixel = i << shift;
318 palette[i].green = colormap->colors[i].green;
319 palette[i].flags = DoGreen;
322 XStoreColors (private->xdisplay, private->xcolormap, palette, size);
324 shift = visual->blue_shift;
325 max_colors = 1 << visual->blue_prec;
326 size = (ncolors < max_colors) ? (ncolors) : (max_colors);
328 for (i = 0; i < size; i++)
330 palette[i].pixel = i << shift;
331 palette[i].blue = colormap->colors[i].blue;
332 palette[i].flags = DoBlue;
335 XStoreColors (private->xdisplay, private->xcolormap, palette, size);
346 gdk_colors_alloc (GdkColormap *colormap,
353 GdkColormapPrivateX *private;
357 g_return_val_if_fail (colormap != NULL, 0);
359 private = (GdkColormapPrivateX*) colormap;
361 return_val = XAllocColorCells (private->xdisplay, private->xcolormap,
362 contiguous, planes, nplanes, pixels, npixels);
366 for (i=0; i<npixels; i++)
368 private->info[pixels[i]].ref_count++;
369 private->info[pixels[i]].flags |= GDK_COLOR_WRITEABLE;
373 return return_val != 0;
377 gdk_color_parse (const gchar *spec,
384 g_return_val_if_fail (spec != NULL, FALSE);
385 g_return_val_if_fail (color != NULL, FALSE);
387 xcolormap = DefaultColormap (gdk_display, gdk_screen);
389 if (XParseColor (gdk_display, xcolormap, spec, &xcolor))
392 color->red = xcolor.red;
393 color->green = xcolor.green;
394 color->blue = xcolor.blue;
402 /* This is almost identical to gdk_colormap_free_colors.
406 gdk_colors_free (GdkColormap *colormap,
411 GdkColormapPrivateX *private;
416 g_return_if_fail (colormap != NULL);
417 g_return_if_fail (in_pixels != NULL);
419 private = (GdkColormapPrivateX*) colormap;
421 if ((private->base.visual->type != GDK_VISUAL_PSEUDO_COLOR) &&
422 (private->base.visual->type != GDK_VISUAL_GRAYSCALE))
425 pixels = g_new (gulong, in_npixels);
427 for (i=0; i<in_npixels; i++)
429 gulong pixel = in_pixels[i];
431 if (private->info[pixel].ref_count)
433 private->info[pixel].ref_count--;
435 if (private->info[pixel].ref_count == 0)
437 pixels[npixels++] = pixel;
438 if (!(private->info[pixel].flags & GDK_COLOR_WRITEABLE))
439 g_hash_table_remove (private->hash, &colormap->colors[pixel]);
440 private->info[pixel].flags = 0;
446 XFreeColors (private->xdisplay, private->xcolormap,
447 pixels, npixels, planes);
451 /* This is almost identical to gdk_colors_free.
455 gdk_colormap_free_colors (GdkColormap *colormap,
459 GdkColormapPrivateX *private;
464 g_return_if_fail (colormap != NULL);
465 g_return_if_fail (colors != NULL);
467 private = (GdkColormapPrivateX*) colormap;
469 if ((private->base.visual->type != GDK_VISUAL_PSEUDO_COLOR) &&
470 (private->base.visual->type != GDK_VISUAL_GRAYSCALE))
473 pixels = g_new (gulong, ncolors);
475 for (i=0; i<ncolors; i++)
477 gulong pixel = colors[i].pixel;
479 if (private->info[pixel].ref_count)
481 private->info[pixel].ref_count--;
483 if (private->info[pixel].ref_count == 0)
485 pixels[npixels++] = pixel;
486 if (!(private->info[pixel].flags & GDK_COLOR_WRITEABLE))
487 g_hash_table_remove (private->hash, &colormap->colors[pixel]);
488 private->info[pixel].flags = 0;
494 XFreeColors (private->xdisplay, private->xcolormap,
500 /********************
502 ********************/
504 /* Try to allocate a single color using XAllocColor. If it succeeds,
505 * cache the result in our colormap, and store in ret.
508 gdk_colormap_alloc1 (GdkColormap *colormap,
512 GdkColormapPrivateX *private;
515 private = (GdkColormapPrivateX*) colormap;
517 xcolor.red = color->red;
518 xcolor.green = color->green;
519 xcolor.blue = color->blue;
520 xcolor.pixel = color->pixel;
521 xcolor.flags = DoRed | DoGreen | DoBlue;
523 if (XAllocColor (private->xdisplay, private->xcolormap, &xcolor))
525 ret->pixel = xcolor.pixel;
526 ret->red = xcolor.red;
527 ret->green = xcolor.green;
528 ret->blue = xcolor.blue;
530 if (ret->pixel < colormap->size)
532 if (private->info[ret->pixel].ref_count) /* got a duplicate */
534 XFreeColors (private->xdisplay, private->xcolormap,
539 colormap->colors[ret->pixel] = *color;
540 colormap->colors[ret->pixel].pixel = ret->pixel;
541 private->info[ret->pixel].ref_count = 1;
543 g_hash_table_insert (private->hash,
544 &colormap->colors[ret->pixel],
545 &colormap->colors[ret->pixel]);
557 gdk_colormap_alloc_colors_writeable (GdkColormap *colormap,
564 GdkColormapPrivateX *private;
569 private = (GdkColormapPrivateX*) colormap;
571 if (private->private_val)
574 for (i=0; i<ncolors; i++)
576 while ((index < colormap->size) && (private->info[index].ref_count != 0))
579 if (index < colormap->size)
581 colors[i].pixel = index;
583 private->info[index].ref_count++;
584 private->info[i].flags |= GDK_COLOR_WRITEABLE;
593 pixels = g_new (gulong, ncolors);
594 /* Allocation of a writeable color cells */
596 status = XAllocColorCells (private->xdisplay, private->xcolormap,
597 FALSE, NULL, 0, pixels, ncolors);
600 for (i=0; i<ncolors; i++)
602 colors[i].pixel = pixels[i];
603 private->info[pixels[i]].ref_count++;
604 private->info[pixels[i]].flags |= GDK_COLOR_WRITEABLE;
610 return status ? ncolors : 0;
615 gdk_colormap_alloc_colors_private (GdkColormap *colormap,
622 GdkColormapPrivateX *private;
624 XColor *store = g_new (XColor, ncolors);
628 private = (GdkColormapPrivateX*) colormap;
631 /* First, store the colors we have room for */
634 for (i=0; i<ncolors; i++)
638 while ((index < colormap->size) && (private->info[index].ref_count != 0))
641 if (index < colormap->size)
643 store[nstore].red = colors[i].red;
644 store[nstore].blue = colors[i].blue;
645 store[nstore].green = colors[i].green;
646 store[nstore].pixel = index;
651 colors[i].pixel = index;
652 private->info[index].ref_count++;
659 XStoreColors (private->xdisplay, private->xcolormap, store, nstore);
662 if (nremaining > 0 && best_match)
664 /* Get best matches for remaining colors */
666 gchar *available = g_new (gchar, colormap->size);
667 for (i = 0; i < colormap->size; i++)
670 for (i=0; i<ncolors; i++)
674 index = gdk_colormap_match_color (colormap,
679 colors[i] = colormap->colors[index];
680 private->info[index].ref_count++;
690 return (ncolors - nremaining);
694 gdk_colormap_alloc_colors_shared (GdkColormap *colormap,
701 GdkColormapPrivateX *private;
706 private = (GdkColormapPrivateX*) colormap;
709 for (i=0; i<ncolors; i++)
713 if (gdk_colormap_alloc1 (colormap, &colors[i], &colors[i]))
721 if (nremaining > 0 && best_match)
723 gchar *available = g_new (gchar, colormap->size);
724 for (i = 0; i < colormap->size; i++)
725 available[i] = ((private->info[i].ref_count == 0) ||
726 !(private->info[i].flags && GDK_COLOR_WRITEABLE));
727 gdk_colormap_sync (colormap, FALSE);
729 while (nremaining > 0)
731 for (i=0; i<ncolors; i++)
735 index = gdk_colormap_match_color (colormap, &colors[i], available);
738 if (private->info[index].ref_count)
740 private->info[index].ref_count++;
741 colors[i] = colormap->colors[index];
747 if (gdk_colormap_alloc1 (colormap,
748 &colormap->colors[index],
757 available[index] = FALSE;
765 success[i] = 2; /* flag as permanent failure */
773 /* Change back the values we flagged as permanent failures */
776 for (i=0; i<ncolors; i++)
779 nremaining = nfailed;
782 return (ncolors - nremaining);
786 gdk_colormap_alloc_colors_pseudocolor (GdkColormap *colormap,
793 GdkColormapPrivateX *private;
794 GdkColor *lookup_color;
798 private = (GdkColormapPrivateX*) colormap;
800 /* Check for an exact match among previously allocated colors */
802 for (i=0; i<ncolors; i++)
806 lookup_color = g_hash_table_lookup (private->hash, &colors[i]);
809 private->info[lookup_color->pixel].ref_count++;
810 colors[i].pixel = lookup_color->pixel;
818 /* If that failed, we try to allocate a new color, or approxmiate
819 * with what we can get if best_match is TRUE.
823 if (private->private_val)
824 return gdk_colormap_alloc_colors_private (colormap, colors, ncolors, writeable, best_match, success);
826 return gdk_colormap_alloc_colors_shared (colormap, colors, ncolors, writeable, best_match, success);
833 gdk_colormap_alloc_colors (GdkColormap *colormap,
840 GdkColormapPrivateX *private;
846 g_return_val_if_fail (colormap != NULL, FALSE);
847 g_return_val_if_fail (colors != NULL, FALSE);
849 private = (GdkColormapPrivateX*) colormap;
851 for (i=0; i<ncolors; i++)
856 switch (private->base.visual->type)
858 case GDK_VISUAL_PSEUDO_COLOR:
859 case GDK_VISUAL_GRAYSCALE:
861 return gdk_colormap_alloc_colors_writeable (colormap, colors, ncolors,
862 writeable, best_match, success);
864 return gdk_colormap_alloc_colors_pseudocolor (colormap, colors, ncolors,
865 writeable, best_match, success);
868 case GDK_VISUAL_DIRECT_COLOR:
869 case GDK_VISUAL_TRUE_COLOR:
870 visual = private->base.visual;
872 for (i=0; i<ncolors; i++)
874 colors[i].pixel = (((colors[i].red >> (16 - visual->red_prec)) << visual->red_shift) +
875 ((colors[i].green >> (16 - visual->green_prec)) << visual->green_shift) +
876 ((colors[i].blue >> (16 - visual->blue_prec)) << visual->blue_shift));
881 case GDK_VISUAL_STATIC_GRAY:
882 case GDK_VISUAL_STATIC_COLOR:
883 for (i=0; i<ncolors; i++)
885 xcolor.red = colors[i].red;
886 xcolor.green = colors[i].green;
887 xcolor.blue = colors[i].blue;
888 xcolor.pixel = colors[i].pixel;
889 xcolor.flags = DoRed | DoGreen | DoBlue;
891 if (XAllocColor (private->xdisplay, private->xcolormap, &xcolor))
893 colors[i].pixel = xcolor.pixel;
905 gdk_color_change (GdkColormap *colormap,
908 GdkColormapPrivateX *private;
911 g_return_val_if_fail (colormap != NULL, FALSE);
912 g_return_val_if_fail (color != NULL, FALSE);
914 xcolor.pixel = color->pixel;
915 xcolor.red = color->red;
916 xcolor.green = color->green;
917 xcolor.blue = color->blue;
918 xcolor.flags = DoRed | DoGreen | DoBlue;
920 private = (GdkColormapPrivateX*) colormap;
921 XStoreColor (private->xdisplay, private->xcolormap, &xcolor);
926 /* XXX: Do not use this function until it is fixed. An X Colormap
927 * is useless unless we also have the visual.
930 gdkx_colormap_get (Colormap xcolormap)
932 GdkColormap *colormap;
933 GdkColormapPrivateX *private;
935 colormap = gdk_colormap_lookup (xcolormap);
939 if (xcolormap == DefaultColormap (gdk_display, gdk_screen))
940 return gdk_colormap_get_system ();
942 private = g_new (GdkColormapPrivateX, 1);
943 colormap = (GdkColormap*) private;
945 private->xdisplay = gdk_display;
946 private->xcolormap = xcolormap;
947 private->base.visual = NULL;
948 private->private_val = TRUE;
950 /* To do the following safely, we would have to have some way of finding
951 * out what the size or visual of the given colormap is. It seems
952 * X doesn't allow this
956 for (i = 0; i < 256; i++)
958 xpalette[i].pixel = i;
960 xpalette[i].green = 0;
961 xpalette[i].blue = 0;
964 XQueryColors (gdk_display, private->xcolormap, xpalette, 256);
966 for (i = 0; i < 256; i++)
968 colormap->colors[i].pixel = xpalette[i].pixel;
969 colormap->colors[i].red = xpalette[i].red;
970 colormap->colors[i].green = xpalette[i].green;
971 colormap->colors[i].blue = xpalette[i].blue;
975 colormap->colors = NULL;
978 gdk_colormap_add (colormap);
985 gdk_colormap_match_color (GdkColormap *cmap,
987 const gchar *available)
991 gint rdiff, gdiff, bdiff;
994 g_return_val_if_fail (cmap != NULL, 0);
995 g_return_val_if_fail (color != NULL, 0);
997 colors = cmap->colors;
1001 for (i = 0; i < cmap->size; i++)
1003 if ((!available) || (available && available[i]))
1005 rdiff = (color->red - colors[i].red);
1006 gdiff = (color->green - colors[i].green);
1007 bdiff = (color->blue - colors[i].blue);
1009 sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
1024 gdk_colormap_lookup (Colormap xcolormap)
1031 cmap = g_hash_table_lookup (colormap_hash, &xcolormap);
1036 gdk_colormap_add (GdkColormap *cmap)
1038 GdkColormapPrivateX *private;
1041 colormap_hash = g_hash_table_new ((GHashFunc) gdk_colormap_hash,
1042 (GCompareFunc) gdk_colormap_cmp);
1044 private = (GdkColormapPrivateX*)cmap;
1046 g_hash_table_insert (colormap_hash, &private->xcolormap, cmap);
1050 gdk_colormap_remove (GdkColormap *cmap)
1052 GdkColormapPrivateX *private;
1055 colormap_hash = g_hash_table_new ((GHashFunc) gdk_colormap_hash,
1056 (GCompareFunc) gdk_colormap_cmp);
1058 private = (GdkColormapPrivateX *)cmap;
1060 g_hash_table_remove (colormap_hash, &private->xcolormap);
1064 gdk_colormap_hash (Colormap *cmap)
1070 gdk_colormap_cmp (Colormap *a,