]> Pileus Git - ~andy/gtk/blob - gdk/gdkcolor.c
a83a7400e6e6717827e2579d3ad709bf1a72f4ce
[~andy/gtk] / gdk / gdkcolor.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
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.
8  *
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.
13  *
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.
18  */
19 #include <X11/Xlib.h>
20 #include "gdk.h"
21 #include "gdkprivate.h"
22
23
24 static gint  gdk_colormap_match_color (GdkColormap *cmap,
25                                        GdkColor    *color,
26                                        const gchar *available);
27 static void  gdk_colormap_add         (GdkColormap *cmap);
28 static void  gdk_colormap_remove      (GdkColormap *cmap);
29 static guint gdk_colormap_hash        (Colormap    *cmap);
30 static gint  gdk_colormap_cmp         (Colormap    *a,
31                                        Colormap    *b);
32 static void gdk_colormap_real_destroy (GdkColormap *colormap);
33
34 static GHashTable *colormap_hash = NULL;
35
36
37 GdkColormap*
38 gdk_colormap_new (GdkVisual *visual,
39                   gint       private_cmap)
40 {
41   GdkColormap *colormap;
42   GdkColormapPrivate *private;
43   Visual *xvisual;
44   int size;
45   int i;
46
47   g_return_val_if_fail (visual != NULL, NULL);
48
49   private = g_new (GdkColormapPrivate, 1);
50   colormap = (GdkColormap*) private;
51
52   private->xdisplay = gdk_display;
53   private->visual = visual;
54   private->next_color = 0;
55   private->ref_count = 1;
56   xvisual = ((GdkVisualPrivate*) visual)->xvisual;
57
58   colormap->size = visual->colormap_size;
59   colormap->colors = g_new (GdkColor, colormap->size);
60
61   switch (visual->type)
62     {
63     case GDK_VISUAL_GRAYSCALE:
64     case GDK_VISUAL_PSEUDO_COLOR:
65       private->private_val = private_cmap;
66       private->xcolormap = XCreateColormap (private->xdisplay, gdk_root_window,
67                                             xvisual, (private_cmap) ? (AllocAll) : (AllocNone));
68
69       if (private_cmap)
70         {
71           XColor *default_colors;
72
73           default_colors = g_new (XColor, colormap->size);
74
75           for (i = 0; i < colormap->size; i++)
76             default_colors[i].pixel = i;
77
78           XQueryColors (private->xdisplay,
79                         DefaultColormap (private->xdisplay, gdk_screen),
80                         default_colors, colormap->size);
81
82           for (i = 0; i < colormap->size; i++)
83             {
84               colormap->colors[i].pixel = default_colors[i].pixel;
85               colormap->colors[i].red = default_colors[i].red;
86               colormap->colors[i].green = default_colors[i].green;
87               colormap->colors[i].blue = default_colors[i].blue;
88             }
89
90           gdk_colormap_change (colormap, colormap->size);
91           
92           g_free (default_colors);
93         }
94       break;
95
96     case GDK_VISUAL_DIRECT_COLOR:
97       private->private_val = TRUE;
98       private->xcolormap = XCreateColormap (private->xdisplay, gdk_root_window,
99                                             xvisual, AllocAll);
100
101       size = 1 << visual->red_prec;
102       for (i = 0; i < size; i++)
103         colormap->colors[i].red = i * 65535 / (size - 1);
104
105       size = 1 << visual->green_prec;
106       for (i = 0; i < size; i++)
107         colormap->colors[i].green = i * 65535 / (size - 1);
108
109       size = 1 << visual->blue_prec;
110       for (i = 0; i < size; i++)
111         colormap->colors[i].blue = i * 65535 / (size - 1);
112
113       gdk_colormap_change (colormap, colormap->size);
114       break;
115
116     case GDK_VISUAL_STATIC_GRAY:
117     case GDK_VISUAL_STATIC_COLOR:
118     case GDK_VISUAL_TRUE_COLOR:
119       private->private_val = FALSE;
120       private->xcolormap = XCreateColormap (private->xdisplay, gdk_root_window,
121                                             xvisual, AllocNone);
122       break;
123     }
124
125   gdk_colormap_add (colormap);
126
127   return colormap;
128 }
129
130 static void
131 gdk_colormap_real_destroy (GdkColormap *colormap)
132 {
133   GdkColormapPrivate *private = (GdkColormapPrivate*) colormap;
134
135   g_return_if_fail (colormap != NULL);
136
137   if (private->ref_count > 0)
138     return;
139
140   gdk_colormap_remove (colormap);
141   XFreeColormap (private->xdisplay, private->xcolormap);
142   g_free (colormap->colors);
143   g_free (colormap);
144 }
145
146 GdkColormap*
147 gdk_colormap_ref (GdkColormap *cmap)
148 {
149   GdkColormapPrivate *private = (GdkColormapPrivate *)cmap;
150   g_return_val_if_fail (cmap != NULL, NULL);
151
152   private->ref_count += 1;
153   return cmap;
154 }
155
156 void
157 gdk_colormap_unref (GdkColormap *cmap)
158 {
159   GdkColormapPrivate *private = (GdkColormapPrivate *)cmap;
160   g_return_if_fail (cmap != NULL);
161
162   private->ref_count -= 1;
163   if (private->ref_count == 0)
164     gdk_colormap_real_destroy (cmap);
165 }
166
167 GdkColormap*
168 gdk_colormap_get_system (void)
169 {
170   static GdkColormap *colormap = NULL;
171   GdkColormapPrivate *private;
172   XColor *xpalette;
173   gint i;
174
175   if (!colormap)
176     {
177       private = g_new (GdkColormapPrivate, 1);
178       colormap = (GdkColormap*) private;
179
180       private->xdisplay = gdk_display;
181       private->xcolormap = DefaultColormap (gdk_display, gdk_screen);
182       private->visual = gdk_visual_get_system ();
183       private->private_val = FALSE;
184       private->next_color = 0;
185       private->ref_count = 1;
186
187       colormap->size = private->visual->colormap_size;
188       colormap->colors = g_new (GdkColor, colormap->size);
189
190       if ((private->visual->type == GDK_VISUAL_GRAYSCALE) ||
191           (private->visual->type == GDK_VISUAL_PSEUDO_COLOR))
192         {
193           xpalette = g_new (XColor, colormap->size);
194           
195           for (i = 0; i < colormap->size; i++)
196             {
197               xpalette[i].pixel = i;
198               xpalette[i].red = 0;
199               xpalette[i].green = 0;
200               xpalette[i].blue = 0;
201             }
202           
203           XQueryColors (gdk_display, private->xcolormap, xpalette, 
204                         colormap->size);
205           
206           for (i = 0; i < colormap->size; i++)
207             {
208               colormap->colors[i].pixel = xpalette[i].pixel;
209               colormap->colors[i].red = xpalette[i].red;
210               colormap->colors[i].green = xpalette[i].green;
211               colormap->colors[i].blue = xpalette[i].blue;
212             }
213
214           g_free (xpalette);
215         }
216
217       gdk_colormap_add (colormap);
218     }
219
220   return colormap;
221 }
222
223 gint
224 gdk_colormap_get_system_size (void)
225 {
226   return DisplayCells (gdk_display, gdk_screen);
227 }
228
229 void
230 gdk_colormap_change (GdkColormap *colormap,
231                      gint         ncolors)
232 {
233   GdkColormapPrivate *private;
234   GdkVisual *visual;
235   XColor *palette;
236   gint shift;
237   int max_colors;
238   int size;
239   int i;
240
241   g_return_if_fail (colormap != NULL);
242
243   palette = g_new (XColor, ncolors);
244
245   private = (GdkColormapPrivate*) colormap;
246   switch (private->visual->type)
247     {
248     case GDK_VISUAL_GRAYSCALE:
249     case GDK_VISUAL_PSEUDO_COLOR:
250       for (i = 0; i < ncolors; i++)
251         {
252           palette[i].pixel = colormap->colors[i].pixel;
253           palette[i].red = colormap->colors[i].red;
254           palette[i].green = colormap->colors[i].green;
255           palette[i].blue = colormap->colors[i].blue;
256           palette[i].flags = DoRed | DoGreen | DoBlue;
257         }
258
259       XStoreColors (private->xdisplay, private->xcolormap, palette, ncolors);
260       private->next_color = MAX (private->next_color, ncolors);
261       break;
262
263     case GDK_VISUAL_DIRECT_COLOR:
264       visual = private->visual;
265
266       shift = visual->red_shift;
267       max_colors = 1 << visual->red_prec;
268       size = (ncolors < max_colors) ? (ncolors) : (max_colors);
269
270       for (i = 0; i < size; i++)
271         {
272           palette[i].pixel = i << shift;
273           palette[i].red = colormap->colors[i].red;
274           palette[i].flags = DoRed;
275         }
276
277       XStoreColors (private->xdisplay, private->xcolormap, palette, size);
278
279       shift = visual->green_shift;
280       max_colors = 1 << visual->green_prec;
281       size = (ncolors < max_colors) ? (ncolors) : (max_colors);
282
283       for (i = 0; i < size; i++)
284         {
285           palette[i].pixel = i << shift;
286           palette[i].green = colormap->colors[i].green;
287           palette[i].flags = DoGreen;
288         }
289
290       XStoreColors (private->xdisplay, private->xcolormap, palette, size);
291
292       shift = visual->blue_shift;
293       max_colors = 1 << visual->blue_prec;
294       size = (ncolors < max_colors) ? (ncolors) : (max_colors);
295
296       for (i = 0; i < size; i++)
297         {
298           palette[i].pixel = i << shift;
299           palette[i].blue = colormap->colors[i].blue;
300           palette[i].flags = DoBlue;
301         }
302
303       XStoreColors (private->xdisplay, private->xcolormap, palette, size);
304       break;
305
306     default:
307       break;
308     }
309
310   g_free (palette);
311 }
312
313 void
314 gdk_colors_store (GdkColormap   *colormap,
315                   GdkColor      *colors,
316                   gint           ncolors)
317 {
318   gint i;
319
320   for (i = 0; i < ncolors; i++)
321     {
322       colormap->colors[i].pixel = colors[i].pixel;
323       colormap->colors[i].red = colors[i].red;
324       colormap->colors[i].green = colors[i].green;
325       colormap->colors[i].blue = colors[i].blue;
326     }
327
328   gdk_colormap_change (colormap, ncolors);
329 }
330
331 gboolean
332 gdk_colors_alloc (GdkColormap   *colormap,
333                   gint           contiguous,
334                   gulong        *planes,
335                   gint           nplanes,
336                   gulong        *pixels,
337                   gint           npixels)
338 {
339   GdkColormapPrivate *private;
340   gint return_val;
341
342   g_return_val_if_fail (colormap != NULL, 0);
343
344   private = (GdkColormapPrivate*) colormap;
345
346   return_val = XAllocColorCells (private->xdisplay, private->xcolormap,
347                                  contiguous, planes, nplanes, pixels, npixels);
348
349   return return_val;
350 }
351
352 void
353 gdk_colors_free (GdkColormap *colormap,
354                  gulong      *pixels,
355                  gint         npixels,
356                  gulong       planes)
357 {
358   GdkColormapPrivate *private;
359
360   g_return_if_fail (colormap != NULL);
361
362   private = (GdkColormapPrivate*) colormap;
363
364   XFreeColors (private->xdisplay, private->xcolormap,
365                pixels, npixels, planes);
366 }
367
368 /*
369  *--------------------------------------------------------------
370  * gdk_color_copy
371  *
372  *   Copy a color structure into new storage.
373  *
374  * Arguments:
375  *   "color" is the color struct to copy.
376  *
377  * Results:
378  *   A new color structure.  Free it with gdk_color_free.
379  *
380  *--------------------------------------------------------------
381  */
382
383 static GMemChunk *color_chunk;
384
385 GdkColor*
386 gdk_color_copy (GdkColor *color)
387 {
388   GdkColor *new_color;
389   
390   g_return_val_if_fail (color != NULL, NULL);
391
392   if (color_chunk == NULL)
393     color_chunk = g_mem_chunk_new ("colors",
394                                    sizeof (GdkColor),
395                                    4096,
396                                    G_ALLOC_AND_FREE);
397
398   new_color = g_chunk_new (GdkColor, color_chunk);
399   *new_color = *color;
400   return new_color;
401 }
402
403 /*
404  *--------------------------------------------------------------
405  * gdk_color_free
406  *
407  *   Free a color structure obtained from gdk_color_copy.  Do not use
408  *   with other color structures.
409  *
410  * Arguments:
411  *   "color" is the color struct to free.
412  *
413  *-------------------------------------------------------------- */
414
415 void
416 gdk_color_free (GdkColor *color)
417 {
418   g_assert (color_chunk != NULL);
419   g_return_if_fail (color != NULL);
420
421   g_mem_chunk_free (color_chunk, color);
422 }
423
424 gint
425 gdk_color_white (GdkColormap *colormap,
426                  GdkColor    *color)
427 {
428   gint return_val;
429
430   g_return_val_if_fail (colormap != NULL, FALSE);
431
432   if (color)
433     {
434       color->pixel = WhitePixel (gdk_display, gdk_screen);
435       color->red = 65535;
436       color->green = 65535;
437       color->blue = 65535;
438
439       return_val = gdk_color_alloc (colormap, color);
440     }
441   else
442     return_val = FALSE;
443
444   return return_val;
445 }
446
447 gint
448 gdk_color_black (GdkColormap *colormap,
449                  GdkColor    *color)
450 {
451   gint return_val;
452
453   g_return_val_if_fail (colormap != NULL, FALSE);
454
455   if (color)
456     {
457       color->pixel = BlackPixel (gdk_display, gdk_screen);
458       color->red = 0;
459       color->green = 0;
460       color->blue = 0;
461
462       return_val = gdk_color_alloc (colormap, color);
463     }
464   else
465     return_val = FALSE;
466
467   return return_val;
468 }
469
470 gboolean
471 gdk_color_parse (const gchar *spec,
472                  GdkColor *color)
473 {
474   Colormap xcolormap;
475   XColor xcolor;
476   gboolean return_val;
477
478   g_return_val_if_fail (spec != NULL, FALSE);
479   g_return_val_if_fail (color != NULL, FALSE);
480
481   xcolormap = DefaultColormap (gdk_display, gdk_screen);
482
483   if (XParseColor (gdk_display, xcolormap, spec, &xcolor))
484     {
485       return_val = TRUE;
486       color->red = xcolor.red;
487       color->green = xcolor.green;
488       color->blue = xcolor.blue;
489     }
490   else
491     return_val = FALSE;
492
493   return return_val;
494 }
495
496 gboolean
497 gdk_color_alloc (GdkColormap *colormap,
498                  GdkColor    *color)
499 {
500   GdkColormapPrivate *private;
501   GdkVisual *visual;
502   XColor xcolor;
503   gchar *available = NULL;
504   gboolean return_val;
505   gint i, index;
506
507   g_return_val_if_fail (colormap != NULL, FALSE);
508   g_return_val_if_fail (color != NULL, FALSE);
509
510   xcolor.red = color->red;
511   xcolor.green = color->green;
512   xcolor.blue = color->blue;
513   xcolor.pixel = color->pixel;
514   xcolor.flags = DoRed | DoGreen | DoBlue;
515
516   return_val = FALSE;
517   private = (GdkColormapPrivate*) colormap;
518
519   switch (private->visual->type)
520     {
521     case GDK_VISUAL_GRAYSCALE:
522     case GDK_VISUAL_PSEUDO_COLOR:
523       if (private->private_val)
524         {
525           if (private->next_color >= colormap->size)
526             {
527               available = g_new (gchar, colormap->size);
528               for (i = 0; i < colormap->size; i++)
529                 available[i] = TRUE;
530
531               index = gdk_colormap_match_color (colormap, color, available);
532               if (index != -1)
533                 {
534                   available[index] = FALSE;
535                   *color = colormap->colors[index];
536                   return_val = TRUE;
537                 }
538               else
539                 {
540                   return_val = FALSE;
541                 }
542             }
543           else
544             {
545               xcolor.pixel = colormap->size - 1 -private->next_color;
546               color->pixel = xcolor.pixel;
547               private->next_color += 1;
548
549               XStoreColor (private->xdisplay, private->xcolormap, &xcolor);
550               return_val = TRUE;
551             }
552         }
553       else
554         {
555           while (1)
556             {
557               if (XAllocColor (private->xdisplay, private->xcolormap, &xcolor))
558                 {
559                   color->pixel = xcolor.pixel;
560                   color->red = xcolor.red;
561                   color->green = xcolor.green;
562                   color->blue = xcolor.blue;
563
564                   if (color->pixel < colormap->size)
565                     colormap->colors[color->pixel] = *color;
566
567                   return_val = TRUE;
568                   break;
569                 }
570               else
571                 {
572                   if (available == NULL)
573                     {
574                       available = g_new (gchar, colormap->size);
575                       for (i = 0; i < colormap->size; i++)
576                         available[i] = TRUE;
577                     }
578
579                   index = gdk_colormap_match_color (colormap, color, available);
580                   if (index != -1)
581                     {
582                       available[index] = FALSE;
583                       xcolor.red = colormap->colors[index].red;
584                       xcolor.green = colormap->colors[index].green;
585                       xcolor.blue = colormap->colors[index].blue;
586                     }
587                   else
588                     {
589                       return_val = FALSE;
590                       break;
591                     }
592                 }
593             }
594         }
595       break;
596
597     case GDK_VISUAL_DIRECT_COLOR:
598       visual = private->visual;
599       xcolor.pixel = (((xcolor.red >> (16 - visual->red_prec)) << visual->red_shift) +
600                       ((xcolor.green >> (16 - visual->green_prec)) << visual->green_shift) +
601                       ((xcolor.blue >> (16 - visual->blue_prec)) << visual->blue_shift));
602       color->pixel = xcolor.pixel;
603       return_val = TRUE;
604       break;
605
606     case GDK_VISUAL_STATIC_GRAY:
607     case GDK_VISUAL_STATIC_COLOR:
608     case GDK_VISUAL_TRUE_COLOR:
609       if (XAllocColor (private->xdisplay, private->xcolormap, &xcolor))
610         {
611           color->pixel = xcolor.pixel;
612           return_val = TRUE;
613         }
614       else
615         return_val = FALSE;
616       break;
617     }
618
619   if (available)
620     g_free (available);
621   
622   return return_val;
623 }
624
625 gboolean
626 gdk_color_change (GdkColormap *colormap,
627                   GdkColor    *color)
628 {
629   GdkColormapPrivate *private;
630   XColor xcolor;
631
632   g_return_val_if_fail (colormap != NULL, FALSE);
633   g_return_val_if_fail (color != NULL, FALSE);
634
635   xcolor.pixel = color->pixel;
636   xcolor.red = color->red;
637   xcolor.green = color->green;
638   xcolor.blue = color->blue;
639   xcolor.flags = DoRed | DoGreen | DoBlue;
640
641   private = (GdkColormapPrivate*) colormap;
642   XStoreColor (private->xdisplay, private->xcolormap, &xcolor);
643
644   return TRUE;
645 }
646
647 gint
648 gdk_color_equal (GdkColor *colora,
649                  GdkColor *colorb)
650 {
651   g_return_val_if_fail (colora != NULL, FALSE);
652   g_return_val_if_fail (colorb != NULL, FALSE);
653
654   return ((colora->red == colorb->red) &&
655           (colora->green == colorb->green) &&
656           (colora->blue == colorb->blue));
657 }
658
659 GdkColormap*
660 gdkx_colormap_get (Colormap xcolormap)
661 {
662   GdkColormap *colormap;
663   GdkColormapPrivate *private;
664
665   colormap = gdk_colormap_lookup (xcolormap);
666   if (colormap)
667     return colormap;
668
669   if (xcolormap == DefaultColormap (gdk_display, gdk_screen))
670     return gdk_colormap_get_system ();
671
672   private = g_new (GdkColormapPrivate, 1);
673   colormap = (GdkColormap*) private;
674
675   private->xdisplay = gdk_display;
676   private->xcolormap = xcolormap;
677   private->visual = NULL;
678   private->private_val = TRUE;
679   private->next_color = 0;
680
681   /* To do the following safely, we would have to have some way of finding
682    * out what the size or visual of the given colormap is. It seems
683    * X doesn't allow this
684    */
685
686 #if 0
687   for (i = 0; i < 256; i++)
688     {
689       xpalette[i].pixel = i;
690       xpalette[i].red = 0;
691       xpalette[i].green = 0;
692       xpalette[i].blue = 0;
693     }
694
695   XQueryColors (gdk_display, private->xcolormap, xpalette, 256);
696
697   for (i = 0; i < 256; i++)
698     {
699       colormap->colors[i].pixel = xpalette[i].pixel;
700       colormap->colors[i].red = xpalette[i].red;
701       colormap->colors[i].green = xpalette[i].green;
702       colormap->colors[i].blue = xpalette[i].blue;
703     }
704 #endif
705
706   colormap->colors = NULL;
707   colormap->size = 0;
708
709   gdk_colormap_add (colormap);
710
711   return colormap;
712 }
713
714
715 static gint
716 gdk_colormap_match_color (GdkColormap *cmap,
717                           GdkColor    *color,
718                           const gchar *available)
719 {
720   GdkColor *colors;
721   guint sum, max;
722   gint rdiff, gdiff, bdiff;
723   gint i, index;
724
725   g_return_val_if_fail (cmap != NULL, 0);
726   g_return_val_if_fail (color != NULL, 0);
727
728   colors = cmap->colors;
729   max = 3 * (65536);
730   index = -1;
731
732   for (i = 0; i < cmap->size; i++)
733     {
734       if ((!available) || (available && available[i]))
735         {
736           rdiff = (color->red - colors[i].red);
737           gdiff = (color->green - colors[i].green);
738           bdiff = (color->blue - colors[i].blue);
739
740           sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
741
742           if (sum < max)
743             {
744               index = i;
745               max = sum;
746             }
747         }
748     }
749
750   return index;
751 }
752
753
754 GdkColormap*
755 gdk_colormap_lookup (Colormap xcolormap)
756 {
757   GdkColormap *cmap;
758
759   if (!colormap_hash)
760     return NULL;
761
762   cmap = g_hash_table_lookup (colormap_hash, &xcolormap);
763   return cmap;
764 }
765
766 static void
767 gdk_colormap_add (GdkColormap *cmap)
768 {
769   GdkColormapPrivate *private;
770
771   if (!colormap_hash)
772     colormap_hash = g_hash_table_new ((GHashFunc) gdk_colormap_hash,
773                                       (GCompareFunc) gdk_colormap_cmp);
774
775   private = (GdkColormapPrivate*) cmap;
776
777   g_hash_table_insert (colormap_hash, &private->xcolormap, cmap);
778 }
779
780 static void
781 gdk_colormap_remove (GdkColormap *cmap)
782 {
783   GdkColormapPrivate *private;
784
785   if (!colormap_hash)
786     colormap_hash = g_hash_table_new ((GHashFunc) gdk_colormap_hash,
787                                       (GCompareFunc) gdk_colormap_cmp);
788
789   private = (GdkColormapPrivate*) cmap;
790
791   g_hash_table_remove (colormap_hash, &private->xcolormap);
792 }
793
794 static guint
795 gdk_colormap_hash (Colormap *cmap)
796 {
797   return *cmap;
798 }
799
800 static gint
801 gdk_colormap_cmp (Colormap *a,
802                   Colormap *b)
803 {
804   return (*a == *b);
805 }