]> Pileus Git - ~andy/gtk/blob - gdk/win32/gdkcolor-win32.c
Add headers. Add section about ActiveIMM.
[~andy/gtk] / gdk / win32 / gdkcolor-win32.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 Lesser 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser 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
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  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/. 
25  */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31
32 #include "gdkcolor.h"
33 #include "gdkinternals.h"
34 #include "gdkprivate-win32.h"
35
36 static void     free_colormap            (Colormap          colormap);
37
38 static gint     gdk_colormap_match_color (GdkColormap      *cmap,
39                                           GdkColor         *color,
40                                           const gchar      *available);
41 static void     gdk_colormap_add         (GdkColormap      *cmap);
42 static void     gdk_colormap_remove      (GdkColormap      *cmap);
43 static guint    gdk_colormap_hash        (Colormap         *cmap);
44 static gboolean gdk_colormap_equal       (Colormap         *a,
45                                           Colormap         *b);
46
47 static void     gdk_colormap_init        (GdkColormap      *colormap);
48 static void     gdk_colormap_class_init  (GdkColormapClass *klass);
49 static void     gdk_colormap_finalize    (GObject          *object);
50
51 static gpointer parent_class = NULL;
52
53 static GHashTable *colormap_hash = NULL;
54
55 GType
56 gdk_colormap_get_type (void)
57 {
58   static GType object_type = 0;
59
60   if (!object_type)
61     {
62       static const GTypeInfo object_info =
63       {
64         sizeof (GdkColormapClass),
65         (GBaseInitFunc) NULL,
66         (GBaseFinalizeFunc) NULL,
67         (GClassInitFunc) gdk_colormap_class_init,
68         NULL,           /* class_finalize */
69         NULL,           /* class_data */
70         sizeof (GdkColormap),
71         0,              /* n_preallocs */
72         (GInstanceInitFunc) gdk_colormap_init,
73       };
74       
75       object_type = g_type_register_static (G_TYPE_OBJECT,
76                                             "GdkColormap",
77                                             &object_info, 0);
78     }
79   
80   return object_type;
81 }
82
83 static void
84 gdk_colormap_init (GdkColormap *colormap)
85 {
86   GdkColormapPrivateWin32 *private;
87
88   private = g_new (GdkColormapPrivateWin32, 1);
89
90   colormap->windowing_data = private;
91   
92   private->hash = NULL;
93   private->last_sync_time = 0;
94   private->info = NULL;
95
96   colormap->size = 0;
97   colormap->colors = NULL;
98 }
99
100 static void
101 gdk_colormap_class_init (GdkColormapClass *klass)
102 {
103   GObjectClass *object_class = G_OBJECT_CLASS (klass);
104
105   parent_class = g_type_class_peek_parent (klass);
106
107   object_class->finalize = gdk_colormap_finalize;
108 }
109
110 static void
111 gdk_colormap_finalize (GObject *object)
112 {
113   GdkColormap *colormap = GDK_COLORMAP (object);
114   GdkColormapPrivateWin32 *private = GDK_COLORMAP_PRIVATE_DATA (colormap);
115
116   gdk_colormap_remove (colormap);
117
118   free_colormap (private->xcolormap);
119
120   if (private->hash)
121     g_hash_table_destroy (private->hash);
122   
123   g_free (private->info);
124   g_free (colormap->colors);
125   
126   G_OBJECT_CLASS (parent_class)->finalize (object);
127 }
128
129 static gboolean
130 alloc_color_cells(Colormap      colormap,
131                   gboolean      contig,
132                   unsigned long plane_masks_return[],
133                   unsigned int  nplanes,
134                   unsigned long pixels_return[],
135                   unsigned int  npixels)
136 {
137   unsigned int i, nfree, iret;
138
139   nfree = 0;
140   for (i = 0; i < colormap->size && nfree < npixels; i++)
141     if (!colormap->in_use[i])
142       nfree++;
143
144   if (colormap->size + npixels - nfree > colormap->sizepalette)
145     {
146       g_warning ("alloc_color_cells: too large palette: %d",
147                  colormap->size + npixels);
148       return FALSE;
149     }
150
151   iret = 0;
152   for (i = 0; i < colormap->size && iret < npixels; i++)
153     if (!colormap->in_use[i])
154       {
155         colormap->in_use[i] = TRUE;
156         pixels_return[iret] = i;
157         iret++;
158       }
159
160   if (nfree < npixels)
161     {
162       int nmore = npixels - nfree;
163
164       /* I don't understand why, if the code below in #if 0 is
165          enabled, gdkrgb fails miserably. The palette doesn't get
166          realized correctly. There doesn't seem to be any harm done by
167          keeping this code out, either.  */
168 #ifdef SOME_STRANGE_BUG
169       if (!ResizePalette (colormap->palette, colormap->size + nmore))
170         {
171           WIN32_GDI_FAILED ("ResizePalette")
172           return FALSE;
173         }
174       g_print("alloc_color_cells: %#x to %d\n",
175               colormap->palette, colormap->size + nmore);
176 #endif
177       for (i = colormap->size; i < colormap->size + nmore; i++)
178         {
179           pixels_return[iret] = i;
180           iret++;
181           colormap->in_use[i] = TRUE;
182         }
183 #ifdef SOME_STRANGE_BUG
184       colormap->size += nmore;
185 #endif
186     }
187   return TRUE;
188 }
189
190 /* The following functions are from Tk8.0, but heavily modified.
191    Here are tk's licensing terms. I hope these terms don't conflict
192    with the GNU Lesser General Public License? They shouldn't, as
193    they are looser that the GLPL, yes? */
194
195 /*
196 This software is copyrighted by the Regents of the University of
197 California, Sun Microsystems, Inc., and other parties.  The following
198 terms apply to all files associated with the software unless explicitly
199 disclaimed in individual files.
200
201 The authors hereby grant permission to use, copy, modify, distribute,
202 and license this software and its documentation for any purpose, provided
203 that existing copyright notices are retained in all copies and that this
204 notice is included verbatim in any distributions. No written agreement,
205 license, or royalty fee is required for any of the authorized uses.
206 Modifications to this software may be copyrighted by their authors
207 and need not follow the licensing terms described here, provided that
208 the new terms are clearly indicated on the first page of each file where
209 they apply.
210
211 IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
212 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
213 ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
214 DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
215 POSSIBILITY OF SUCH DAMAGE.
216
217 THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
218 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
219 FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
220 IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
221 NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
222 MODIFICATIONS.
223
224 GOVERNMENT USE: If you are acquiring this software on behalf of the
225 U.S. government, the Government shall have only "Restricted Rights"
226 in the software and related documentation as defined in the Federal 
227 Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
228 are acquiring the software on behalf of the Department of Defense, the
229 software shall be classified as "Commercial Computer Software" and the
230 Government shall have only "Restricted Rights" as defined in Clause
231 252.227-7013 (c) (1) of DFARs.  Notwithstanding the foregoing, the
232 authors grant the U.S. Government and others acting in its behalf
233 permission to use and distribute the software in accordance with the
234 terms specified in this license.
235 */
236 /*
237  *----------------------------------------------------------------------
238  *
239  * XAllocColor --
240  *
241  *      Find the closest available color to the specified XColor.
242  *
243  * Results:
244  *      Updates the color argument and returns 1 on success.  Otherwise
245  *      returns 0.
246  *
247  * Side effects:
248  *      Allocates a new color in the palette.
249  *
250  *----------------------------------------------------------------------
251  */
252
253 static int
254 alloc_color(Colormap  colormap,
255             XColor   *color,
256             guint    *pixelp)
257 {
258   PALETTEENTRY entry, closeEntry;
259   unsigned int i;
260     
261   entry = *color;
262   entry.peFlags = 0;
263
264   if (colormap->rc_palette)
265     {
266       COLORREF newPixel, closePixel;
267       UINT index;
268
269       /*
270        * Find the nearest existing palette entry.
271        */
272         
273       newPixel = RGB (entry.peRed, entry.peGreen, entry.peBlue);
274       index = GetNearestPaletteIndex (colormap->palette, newPixel);
275       GetPaletteEntries (colormap->palette, index, 1, &closeEntry);
276       closePixel = RGB (closeEntry.peRed, closeEntry.peGreen,
277                         closeEntry.peBlue);
278
279       if (newPixel != closePixel)
280         {
281           /* Not a perfect match. */
282           if (!colormap->in_use[index])
283             {
284               /* It was a free'd entry anyway, so we can use it, and
285                  set it to the correct color. */
286               if (SetPaletteEntries (colormap->palette, index, 1, &entry) == 0)
287                 WIN32_GDI_FAILED ("SetPaletteEntries");
288             }
289           else
290             {
291               /* The close entry found is in use, so search for a
292                  unused slot. */
293                  
294               for (i = 0; i < colormap->size; i++)
295                 if (!colormap->in_use[i])
296                   {
297                     /* A free slot, use it. */
298                     if (SetPaletteEntries (colormap->palette,
299                                            index, 1, &entry) == 0)
300                       WIN32_GDI_FAILED ("SetPaletteEntries");
301                     index = i;
302                     break;
303                   }
304               if (i == colormap->size)
305                 {
306                   /* No free slots found. If the palette isn't maximal
307                      yet, grow it. */
308                   if (colormap->size == colormap->sizepalette)
309                     {
310                       /* The palette is maximal, and no free slots available,
311                          so use the close entry, then, dammit. */
312                       *color = closeEntry;
313                     }
314                   else
315                     {
316                       /* There is room to grow the palette. */
317                       index = colormap->size;
318                       colormap->size++;
319                       if (!ResizePalette (colormap->palette, colormap->size))
320                         WIN32_GDI_FAILED ("ResizePalette");
321                       if (SetPaletteEntries (colormap->palette, index, 1, &entry) == 0)
322                         WIN32_GDI_FAILED ("SetPaletteEntries");
323                     }
324                 }
325             }
326           colormap->stale = TRUE;
327         }
328       else
329         {
330           /* We got a match, so use it. */
331         }
332
333       *pixelp = index;
334       colormap->in_use[index] = TRUE;
335 #if 0
336       g_print("alloc_color from %#x: index %d for %02x %02x %02x\n",
337               colormap->palette, index,
338               entry.peRed, entry.peGreen, entry.peBlue);
339 #endif
340     }
341   else
342     {
343       /*
344        * Determine what color will actually be used on non-colormap systems.
345        */
346       *pixelp = GetNearestColor (gdk_display_hdc, RGB(entry.peRed, entry.peGreen, entry.peBlue));
347       
348       color->peRed = GetRValue (*pixelp);
349       color->peGreen = GetGValue (*pixelp);
350       color->peBlue = GetBValue (*pixelp);
351     }
352   
353   return 1;
354 }
355
356 /*
357  *----------------------------------------------------------------------
358  *
359  * XFreeColors --
360  *
361  *      Deallocate a block of colors.
362  *
363  * Results:
364  *      None.
365  *
366  * Side effects:
367  *      Removes entries for the current palette and compacts the
368  *      remaining set.
369  *
370  *----------------------------------------------------------------------
371  */
372
373 static void
374 free_colors (Colormap colormap,
375              gulong  *pixels,
376              gint     npixels,
377              gulong   planes)
378 {
379   gint i;
380   PALETTEENTRY entries[256];
381
382   /*
383    * We don't have to do anything for non-palette devices.
384    */
385   
386   if (colormap->rc_palette)
387     {
388       int npal;
389       int lowestpixel = 256;
390       int highestpixel = -1;
391
392       npal = GetPaletteEntries (colormap->palette, 0, 256, entries);
393       for (i = 0; i < npixels; i++)
394         {
395           int pixel = pixels[i];
396
397           if (pixel < lowestpixel)
398             lowestpixel = pixel;
399           if (pixel > highestpixel)
400             highestpixel = pixel;
401
402           colormap->in_use[pixel] = FALSE;
403
404           entries[pixel] = entries[0];
405         }
406 #if 0
407       if (SetPaletteEntries (colormap->palette, lowestpixel,
408                              highestpixel - lowestpixel + 1,
409                              entries + lowestpixel) == 0)
410         WIN32_GDI_FAILED ("SetPaletteEntries");
411 #endif
412       colormap->stale = TRUE;
413 #if 0
414       g_print("free_colors %#x lowestpixel = %d, highestpixel = %d\n",
415               colormap->palette, lowestpixel, highestpixel);
416 #endif
417     }
418 }
419
420 /*
421  *----------------------------------------------------------------------
422  *
423  * XCreateColormap --
424  *
425  *      Allocate a new colormap.
426  *
427  * Results:
428  *      Returns a newly allocated colormap.
429  *
430  * Side effects:
431  *      Allocates an empty palette and color list.
432  *
433  *----------------------------------------------------------------------
434  */
435
436 static Colormap
437 create_colormap (HWND     w,
438                  Visual  *visual,
439                  gboolean alloc)
440 {
441   char logPalBuf[sizeof(LOGPALETTE) + 256 * sizeof(PALETTEENTRY)];
442   LOGPALETTE *logPalettePtr;
443   Colormap colormap;
444   guint i;
445   HPALETTE sysPal;
446   HDC hdc;
447
448   /* Should the alloc parameter do something? */
449
450
451   /* Allocate a starting palette with all of the reserved colors. */
452   
453   logPalettePtr = (LOGPALETTE *) logPalBuf;
454   logPalettePtr->palVersion = 0x300;
455   sysPal = (HPALETTE) GetStockObject (DEFAULT_PALETTE);
456   logPalettePtr->palNumEntries =
457     GetPaletteEntries (sysPal, 0, 256, logPalettePtr->palPalEntry);
458   
459   colormap = (Colormap) g_new (ColormapStruct, 1);
460   colormap->size = logPalettePtr->palNumEntries;
461   colormap->stale = TRUE;
462   colormap->palette = CreatePalette (logPalettePtr);
463   hdc = GetDC (NULL);
464   colormap->rc_palette = ((GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) != 0);
465   if (colormap->rc_palette)
466     {
467       colormap->sizepalette = GetDeviceCaps (hdc, SIZEPALETTE);
468       colormap->in_use = g_new (gboolean, colormap->sizepalette);
469       /* Mark static colors in use. */
470       for (i = 0; i < logPalettePtr->palNumEntries; i++)
471         colormap->in_use[i] = TRUE;
472       /* Mark rest not in use */
473       for (i = logPalettePtr->palNumEntries; i < colormap->sizepalette; i++)
474         colormap->in_use[i] = FALSE;
475     }
476   ReleaseDC (NULL, hdc);
477
478   return colormap;
479 }
480
481 /*
482  *----------------------------------------------------------------------
483  *
484  * XFreeColormap --
485  *
486  *      Frees the resources associated with the given colormap.
487  *
488  * Results:
489  *      None.
490  *
491  * Side effects:
492  *      Deletes the palette associated with the colormap.  Note that
493  *      the palette must not be selected into a device context when
494  *      this occurs.
495  *
496  *----------------------------------------------------------------------
497  */
498
499 static void
500 free_colormap(Colormap colormap)
501  
502 {
503   if (!DeleteObject (colormap->palette))
504     {
505       g_error ("Unable to free colormap, palette is still selected.");
506     }
507   g_free (colormap);
508 }
509
510 static Colormap
511 default_colormap ()
512 {
513   static Colormap colormap;
514
515   if (colormap)
516     return colormap;
517
518   colormap = create_colormap ( NULL, NULL, FALSE);
519   return colormap;
520 }
521
522 GdkColormap*
523 gdk_colormap_new (GdkVisual *visual,
524                   gboolean   private_cmap)
525 {
526   GdkColormap *colormap;
527   GdkColormapPrivateWin32 *private;
528   Visual *xvisual;
529   int i;
530
531   g_return_val_if_fail (visual != NULL, NULL);
532
533   colormap = g_object_new (gdk_colormap_get_type (), NULL);
534   private = GDK_COLORMAP_PRIVATE_DATA (colormap);
535
536   colormap->visual = visual;
537
538   xvisual = ((GdkVisualPrivate*) visual)->xvisual;
539
540   colormap->size = visual->colormap_size;
541
542   switch (visual->type)
543     {
544     case GDK_VISUAL_GRAYSCALE:
545     case GDK_VISUAL_PSEUDO_COLOR:
546       private->info = g_new0 (GdkColorInfo, colormap->size);
547       colormap->colors = g_new (GdkColor, colormap->size);
548       
549       private->hash = g_hash_table_new ((GHashFunc) gdk_color_hash,
550                                         (GEqualFunc) gdk_color_equal);
551       
552       private->private_val = private_cmap;
553       private->xcolormap = create_colormap (gdk_root_window, xvisual, private_cmap);
554
555       if (private_cmap)
556         {
557           PALETTEENTRY pal[256];
558           guint npal;
559
560           npal = GetPaletteEntries (private->xcolormap->palette, 0, colormap->size, pal);
561           for (i = 0; i < colormap->size; i++)
562             {
563               colormap->colors[i].pixel = i;
564               if (i >= npal)
565                 {
566                   colormap->colors[i].red =
567                     colormap->colors[i].green =
568                     colormap->colors[i].blue = 0;
569                 }
570               else
571                 {
572                   colormap->colors[i].red = (pal[i].peRed * 65535) / 255;
573                   colormap->colors[i].green = (pal[i].peGreen * 65525) / 255;
574                   colormap->colors[i].blue = (pal[i].peBlue * 65535) / 255;
575                 }
576             }
577           gdk_colormap_change (colormap, colormap->size);
578         }
579       break;
580
581     case GDK_VISUAL_STATIC_GRAY:
582     case GDK_VISUAL_STATIC_COLOR:
583     case GDK_VISUAL_TRUE_COLOR:
584       private->private_val = FALSE;
585       private->xcolormap = create_colormap (gdk_root_window,
586                                             xvisual, FALSE);
587       break;
588
589     case GDK_VISUAL_DIRECT_COLOR:
590       g_assert_not_reached ();
591     }
592
593   gdk_colormap_add (colormap);
594
595   return colormap;
596 }
597
598 #define MIN_SYNC_TIME 2
599
600 static void
601 gdk_colormap_sync (GdkColormap *colormap,
602                    gboolean     force)
603 {
604   time_t current_time;
605   GdkColormapPrivateWin32 *private = GDK_COLORMAP_PRIVATE_DATA (colormap);
606   XColor *xpalette;
607   gint nlookup;
608   gint i;
609   
610   g_return_if_fail (colormap != NULL);
611
612   current_time = time (NULL);
613   if (!force && ((current_time - private->last_sync_time) < MIN_SYNC_TIME))
614     return;
615
616   private->last_sync_time = current_time;
617
618   nlookup = 0;
619   xpalette = g_new (XColor, colormap->size);
620   
621   nlookup = GetPaletteEntries (private->xcolormap->palette,
622                                0, colormap->size, xpalette);
623   
624   for (i = 0; i < nlookup; i++)
625     {
626       colormap->colors[i].pixel = i;
627       colormap->colors[i].red = (xpalette[i].peRed * 65535) / 255;
628       colormap->colors[i].green = (xpalette[i].peGreen * 65535) / 255;
629       colormap->colors[i].blue = (xpalette[i].peBlue * 65535) / 255;
630     }
631
632   for (  ; i < colormap->size; i++)
633     {
634       colormap->colors[i].pixel = i;
635       colormap->colors[i].red = 0;
636       colormap->colors[i].green = 0;
637       colormap->colors[i].blue = 0;
638     }
639
640   g_free (xpalette);
641 }
642                    
643 GdkColormap*
644 gdk_colormap_get_system (void)
645 {
646   static GdkColormap *colormap = NULL;
647   GdkColormapPrivateWin32 *private;
648
649   if (!colormap)
650     {
651       colormap = g_object_new (gdk_colormap_get_type (), NULL);
652       private = GDK_COLORMAP_PRIVATE_DATA (colormap);
653
654       private->xcolormap = default_colormap ();
655       colormap->visual = gdk_visual_get_system ();
656       private->private_val = FALSE;
657
658       private->hash = NULL;
659       private->last_sync_time = 0;
660       private->info = NULL;
661
662       colormap->colors = NULL;
663       colormap->size = colormap->visual->colormap_size;
664
665       if ((colormap->visual->type == GDK_VISUAL_GRAYSCALE) ||
666           (colormap->visual->type == GDK_VISUAL_PSEUDO_COLOR))
667         {
668           private->info = g_new0 (GdkColorInfo, colormap->size);
669           colormap->colors = g_new (GdkColor, colormap->size);
670           
671           private->hash = g_hash_table_new ((GHashFunc) gdk_color_hash,
672                                             (GEqualFunc) gdk_color_equal);
673
674           gdk_colormap_sync (colormap, TRUE);
675         }
676       gdk_colormap_add (colormap);
677     }
678
679   return colormap;
680 }
681
682 gint
683 gdk_colormap_get_system_size (void)
684 {
685   gint bitspixel;
686   
687   bitspixel = GetDeviceCaps (gdk_display_hdc, BITSPIXEL);
688
689   if (bitspixel == 1)
690     return 2;
691   else if (bitspixel == 4)
692     return 16;
693   else if (bitspixel == 8)
694     return 256;
695   else if (bitspixel == 12)
696     return 32;
697   else if (bitspixel == 16)
698     return 64;
699   else /* if (bitspixel >= 24) */
700     return 256;
701 }
702
703 void
704 gdk_colormap_change (GdkColormap *colormap,
705                      gint         ncolors)
706 {
707   GdkColormapPrivateWin32 *private;
708   XColor *palette;
709   int i;
710
711   g_return_if_fail (GDK_IS_COLORMAP (colormap));
712
713   palette = g_new (XColor, ncolors);
714
715   private = GDK_COLORMAP_PRIVATE_DATA (colormap);
716   switch (colormap->visual->type)
717     {
718     case GDK_VISUAL_GRAYSCALE:
719     case GDK_VISUAL_PSEUDO_COLOR:
720       for (i = 0; i < ncolors; i++)
721         {
722           palette[i].peRed = (colormap->colors[i].red >> 8);
723           palette[i].peGreen = (colormap->colors[i].green >> 8);
724           palette[i].peBlue = (colormap->colors[i].blue >> 8);
725           palette[i].peFlags = 0;
726         }
727
728       if (SetPaletteEntries (private->xcolormap->palette,
729                              0, ncolors, palette) == 0)
730         WIN32_GDI_FAILED ("SetPaletteEntries");
731       private->xcolormap->stale = TRUE;
732       break;
733
734     default:
735       break;
736     }
737
738   g_free (palette);
739 }
740
741 gboolean
742 gdk_colors_alloc (GdkColormap   *colormap,
743                   gboolean       contiguous,
744                   gulong        *planes,
745                   gint           nplanes,
746                   gulong        *pixels,
747                   gint           npixels)
748 {
749   GdkColormapPrivateWin32 *private;
750   gint return_val;
751   gint i;
752
753   g_return_val_if_fail (GDK_IS_COLORMAP (colormap), 0);
754
755   private = GDK_COLORMAP_PRIVATE_DATA (colormap);
756
757   return_val = alloc_color_cells (private->xcolormap, contiguous,
758                                   planes, nplanes, pixels, npixels);
759
760   if (return_val)
761     {
762       for (i=0; i<npixels; i++)
763         {
764           private->info[pixels[i]].ref_count++;
765           private->info[pixels[i]].flags |= GDK_COLOR_WRITEABLE;
766         }
767     }
768
769   return return_val != 0;
770 }
771
772 /* This is almost identical to gdk_colormap_free_colors.
773  * Keep them in sync!
774  */
775 void
776 gdk_colors_free (GdkColormap *colormap,
777                  gulong      *in_pixels,
778                  gint         in_npixels,
779                  gulong       planes)
780 {
781   GdkColormapPrivateWin32 *private;
782   gulong *pixels;
783   gint npixels = 0;
784   gint i;
785
786   g_return_if_fail (GDK_IS_COLORMAP (colormap));
787   g_return_if_fail (in_pixels != NULL);
788
789   private = GDK_COLORMAP_PRIVATE_DATA (colormap);
790
791   if ((colormap->visual->type != GDK_VISUAL_PSEUDO_COLOR) &&
792       (colormap->visual->type != GDK_VISUAL_GRAYSCALE))
793     return;
794   
795   pixels = g_new (gulong, in_npixels);
796
797   for (i=0; i<in_npixels; i++)
798     {
799       gulong pixel = in_pixels[i];
800       
801       if (private->info[pixel].ref_count)
802         {
803           private->info[pixel].ref_count--;
804
805           if (private->info[pixel].ref_count == 0)
806             {
807               pixels[npixels++] = pixel;
808               if (!(private->info[pixel].flags & GDK_COLOR_WRITEABLE))
809                 g_hash_table_remove (private->hash, &colormap->colors[pixel]);
810               private->info[pixel].flags = 0;
811             }
812         }
813     }
814
815   if (npixels)
816     free_colors (private->xcolormap, pixels, npixels, planes);
817
818   g_free (pixels);
819 }
820
821 /* This is almost identical to gdk_colors_free.
822  * Keep them in sync!
823  */
824 void
825 gdk_colormap_free_colors (GdkColormap *colormap,
826                           GdkColor    *colors,
827                           gint         ncolors)
828 {
829   GdkColormapPrivateWin32 *private;
830   gulong *pixels;
831   gint npixels = 0;
832   gint i;
833
834   g_return_if_fail (GDK_IS_COLORMAP (colormap));
835   g_return_if_fail (colors != NULL);
836
837   private = GDK_COLORMAP_PRIVATE_DATA (colormap);
838
839   if ((colormap->visual->type != GDK_VISUAL_PSEUDO_COLOR) &&
840       (colormap->visual->type != GDK_VISUAL_GRAYSCALE))
841     return;
842
843   pixels = g_new (gulong, ncolors);
844
845   for (i=0; i<ncolors; i++)
846     {
847       gulong pixel = colors[i].pixel;
848       
849       if (private->info[pixel].ref_count)
850         {
851           private->info[pixel].ref_count--;
852
853           if (private->info[pixel].ref_count == 0)
854             {
855               pixels[npixels++] = pixel;
856               if (!(private->info[pixel].flags & GDK_COLOR_WRITEABLE))
857                 g_hash_table_remove (private->hash, &colormap->colors[pixel]);
858               private->info[pixel].flags = 0;
859             }
860         }
861     }
862   if (npixels)
863     free_colors (private->xcolormap, pixels, npixels, 0);
864   g_free (pixels);
865 }
866
867 /********************
868  * Color allocation *
869  ********************/
870
871 /* Try to allocate a single color using alloc_color. If it succeeds,
872  * cache the result in our colormap, and store in ret.
873  */
874 static gboolean 
875 gdk_colormap_alloc1 (GdkColormap *colormap,
876                      GdkColor    *color,
877                      GdkColor    *ret)
878 {
879   GdkColormapPrivateWin32 *private;
880   XColor xcolor;
881
882   private = GDK_COLORMAP_PRIVATE_DATA (colormap);
883
884   xcolor.peRed = color->red >> 8;
885   xcolor.peGreen = color->green >> 8;
886   xcolor.peBlue = color->blue >> 8;
887
888   if (alloc_color (private->xcolormap, &xcolor, &ret->pixel))
889     {
890       ret->red = (xcolor.peRed * 65535) / 255;
891       ret->green = (xcolor.peGreen * 65535) / 255;
892       ret->blue = (xcolor.peBlue * 65535) / 255;
893       
894       if ((guint) ret->pixel < colormap->size)
895         {
896           if (private->info[ret->pixel].ref_count) /* got a duplicate */
897             {
898               /* XXX */
899             }
900           else
901             {
902               colormap->colors[ret->pixel] = *color;
903               private->info[ret->pixel].ref_count = 1;
904
905               g_hash_table_insert (private->hash,
906                                    &colormap->colors[ret->pixel],
907                                    &colormap->colors[ret->pixel]);
908             }
909         }
910       return TRUE;
911     }
912   else
913     {
914       return FALSE;
915     }
916 }
917
918 static gint
919 gdk_colormap_alloc_colors_writeable (GdkColormap *colormap,
920                                      GdkColor    *colors,
921                                      gint         ncolors,
922                                      gboolean     writeable,
923                                      gboolean     best_match,
924                                      gboolean    *success)
925 {
926   GdkColormapPrivateWin32 *private;
927   gulong *pixels;
928   gboolean status;
929   gint i, index;
930
931   private = GDK_COLORMAP_PRIVATE_DATA (colormap);
932
933   if (private->private_val)
934     {
935       index = 0;
936       for (i=0; i<ncolors; i++)
937         {
938           while ((index < colormap->size) && (private->info[index].ref_count != 0))
939             index++;
940           
941           if (index < colormap->size)
942             {
943               colors[i].pixel = index;
944               success[i] = TRUE;
945               private->info[index].ref_count++;
946               private->info[i].flags |= GDK_COLOR_WRITEABLE;
947             }
948           else
949             break;
950         }
951       return i;
952     }
953   else
954     {
955       pixels = g_new (gulong, ncolors);
956       /* Allocation of a writeable color cells */
957       
958       status =  alloc_color_cells (private->xcolormap, FALSE, NULL,
959                                    0, pixels, ncolors);
960       if (status)
961         {
962           for (i=0; i<ncolors; i++)
963             {
964               colors[i].pixel = pixels[i];
965               private->info[pixels[i]].ref_count++;
966               private->info[pixels[i]].flags |= GDK_COLOR_WRITEABLE;
967             }
968         }
969       
970       g_free (pixels);
971
972       return status ? ncolors : 0; 
973     }
974 }
975
976 static gint
977 gdk_colormap_alloc_colors_private (GdkColormap *colormap,
978                                    GdkColor    *colors,
979                                    gint         ncolors,
980                                    gboolean     writeable,
981                                    gboolean     best_match,
982                                    gboolean    *success)
983 {
984   GdkColormapPrivateWin32 *private;
985   gint i, index;
986   XColor *store = g_new (XColor, ncolors);
987   gint nstore = 0;
988   gint nremaining = 0;
989   
990   private = GDK_COLORMAP_PRIVATE_DATA (colormap);
991   index = -1;
992
993   /* First, store the colors we have room for */
994
995   index = 0;
996   for (i=0; i<ncolors; i++)
997     {
998       if (!success[i])
999         {
1000           while ((index < colormap->size) && (private->info[index].ref_count != 0))
1001             index++;
1002
1003           if (index < colormap->size)
1004             {
1005               store[nstore].peRed = colors[i].red >> 8;
1006               store[nstore].peBlue = colors[i].blue >> 8;
1007               store[nstore].peGreen = colors[i].green >> 8;
1008               nstore++;
1009
1010               success[i] = TRUE;
1011
1012               colors[i].pixel = index;
1013               private->info[index].ref_count++;
1014             }
1015           else
1016             nremaining++;
1017         }
1018     }
1019   
1020   if (SetPaletteEntries (private->xcolormap->palette,
1021                          0, nstore, store) == 0)
1022     WIN32_GDI_FAILED ("SetPaletteEntries");
1023   private->xcolormap->stale = TRUE;
1024
1025   g_free (store);
1026
1027   if (nremaining > 0 && best_match)
1028     {
1029       /* Get best matches for remaining colors */
1030
1031       gchar *available = g_new (gchar, colormap->size);
1032       for (i = 0; i < colormap->size; i++)
1033         available[i] = TRUE;
1034
1035       for (i=0; i<ncolors; i++)
1036         {
1037           if (!success[i])
1038             {
1039               index = gdk_colormap_match_color (colormap, 
1040                                                 &colors[i], 
1041                                                 available);
1042               if (index != -1)
1043                 {
1044                   colors[i] = colormap->colors[index];
1045                   private->info[index].ref_count++;
1046
1047                   success[i] = TRUE;
1048                   nremaining--;
1049                 }
1050             }
1051         }
1052       g_free (available);
1053     }
1054
1055   return (ncolors - nremaining);
1056 }
1057
1058 static gint
1059 gdk_colormap_alloc_colors_shared (GdkColormap *colormap,
1060                                   GdkColor    *colors,
1061                                   gint         ncolors,
1062                                   gboolean     writeable,
1063                                   gboolean     best_match,
1064                                   gboolean    *success)
1065 {
1066   GdkColormapPrivateWin32 *private;
1067   gint i, index;
1068   gint nremaining = 0;
1069   gint nfailed = 0;
1070
1071   private = GDK_COLORMAP_PRIVATE_DATA (colormap);
1072   index = -1;
1073
1074   for (i=0; i<ncolors; i++)
1075     {
1076       if (!success[i])
1077         {
1078           if (gdk_colormap_alloc1 (colormap, &colors[i], &colors[i]))
1079             success[i] = TRUE;
1080           else
1081             nremaining++;
1082         }
1083     }
1084
1085
1086   if (nremaining > 0 && best_match)
1087     {
1088       gchar *available = g_new (gchar, colormap->size);
1089       for (i = 0; i < colormap->size; i++)
1090         available[i] = ((private->info[i].ref_count == 0) ||
1091                         !(private->info[i].flags & GDK_COLOR_WRITEABLE));
1092       gdk_colormap_sync (colormap, FALSE);
1093       
1094       while (nremaining > 0)
1095         {
1096           for (i=0; i<ncolors; i++)
1097             {
1098               if (!success[i])
1099                 {
1100                   index = gdk_colormap_match_color (colormap, &colors[i], available);
1101                   if (index != -1)
1102                     {
1103                       if (private->info[index].ref_count)
1104                         {
1105                           private->info[index].ref_count++;
1106                           colors[i] = colormap->colors[index];
1107                           success[i] = TRUE;
1108                           nremaining--;
1109                         }
1110                       else
1111                         {
1112                           if (gdk_colormap_alloc1 (colormap, 
1113                                                    &colormap->colors[index],
1114                                                    &colors[i]))
1115                             {
1116                               success[i] = TRUE;
1117                               nremaining--;
1118                               break;
1119                             }
1120                           else
1121                             {
1122                               available[index] = FALSE;
1123                             }
1124                         }
1125                     }
1126                   else
1127                     {
1128                       nfailed++;
1129                       nremaining--;
1130                       success[i] = 2; /* flag as permanent failure */
1131                     }
1132                 }
1133             }
1134         }
1135       g_free (available);
1136     }
1137
1138   /* Change back the values we flagged as permanent failures */
1139   if (nfailed > 0)
1140     {
1141       for (i=0; i<ncolors; i++)
1142         if (success[i] == 2)
1143           success[i] = FALSE;
1144       nremaining = nfailed;
1145     }
1146   
1147   return (ncolors - nremaining);
1148 }
1149
1150 static gint
1151 gdk_colormap_alloc_colors_pseudocolor (GdkColormap *colormap,
1152                                        GdkColor    *colors,
1153                                        gint         ncolors,
1154                                        gboolean     writeable,
1155                                        gboolean     best_match,
1156                                        gboolean    *success)
1157 {
1158   GdkColormapPrivateWin32 *private;
1159   GdkColor *lookup_color;
1160   gint i;
1161   gint nremaining = 0;
1162
1163   private = GDK_COLORMAP_PRIVATE_DATA (colormap);
1164
1165   /* Check for an exact match among previously allocated colors */
1166
1167   for (i=0; i<ncolors; i++)
1168     {
1169       if (!success[i])
1170         {
1171           lookup_color = g_hash_table_lookup (private->hash, &colors[i]);
1172           if (lookup_color)
1173             {
1174               private->info[lookup_color->pixel].ref_count++;
1175               colors[i].pixel = lookup_color->pixel;
1176               success[i] = TRUE;
1177             }
1178           else
1179             nremaining++;
1180         }
1181     }
1182
1183   /* If that failed, we try to allocate a new color, or approxmiate
1184    * with what we can get if best_match is TRUE.
1185    */
1186   if (nremaining > 0)
1187     {
1188       if (private->private_val)
1189         return gdk_colormap_alloc_colors_private (colormap, colors, ncolors, writeable, best_match, success);
1190       else
1191         return gdk_colormap_alloc_colors_shared (colormap, colors, ncolors, writeable, best_match, success);
1192     }
1193   else
1194     return 0;
1195 }
1196
1197 gint
1198 gdk_colormap_alloc_colors (GdkColormap *colormap,
1199                            GdkColor    *colors,
1200                            gint         ncolors,
1201                            gboolean     writeable,
1202                            gboolean     best_match,
1203                            gboolean    *success)
1204 {
1205   GdkColormapPrivateWin32 *private;
1206   GdkVisual *visual;
1207   gint i;
1208   gint nremaining = 0;
1209   XColor xcolor;
1210
1211   g_return_val_if_fail (GDK_IS_COLORMAP (colormap), FALSE);
1212   g_return_val_if_fail (colors != NULL, FALSE);
1213
1214   private = GDK_COLORMAP_PRIVATE_DATA (colormap);
1215
1216   for (i=0; i<ncolors; i++)
1217     {
1218       success[i] = FALSE;
1219     }
1220
1221   switch (colormap->visual->type)
1222     {
1223     case GDK_VISUAL_PSEUDO_COLOR:
1224     case GDK_VISUAL_GRAYSCALE:
1225       if (writeable)
1226         return gdk_colormap_alloc_colors_writeable (colormap, colors, ncolors,
1227                                                     writeable, best_match, success);
1228       else
1229         return gdk_colormap_alloc_colors_pseudocolor (colormap, colors, ncolors,
1230                                                     writeable, best_match, success);
1231       break;
1232
1233     case GDK_VISUAL_TRUE_COLOR:
1234       visual = colormap->visual;
1235
1236       for (i=0; i<ncolors; i++)
1237         {
1238           colors[i].pixel = (((colors[i].red >> (16 - visual->red_prec)) << visual->red_shift) +
1239                              ((colors[i].green >> (16 - visual->green_prec)) << visual->green_shift) +
1240                              ((colors[i].blue >> (16 - visual->blue_prec)) << visual->blue_shift));
1241           success[i] = TRUE;
1242         }
1243       break;
1244
1245     case GDK_VISUAL_STATIC_GRAY:
1246     case GDK_VISUAL_STATIC_COLOR:
1247       for (i=0; i<ncolors; i++)
1248         {
1249           xcolor.peRed = colors[i].red >> 8;
1250           xcolor.peGreen = colors[i].green >> 8;
1251           xcolor.peBlue = colors[i].blue >> 8;
1252           if (alloc_color (private->xcolormap, &xcolor, &colors[i].pixel))
1253             success[i] = TRUE;
1254           else
1255             nremaining++;
1256         }
1257       break;
1258
1259     case GDK_VISUAL_DIRECT_COLOR:
1260       g_assert_not_reached ();
1261     }
1262   return nremaining;
1263 }
1264
1265 void
1266 gdk_colormap_query_color (GdkColormap *colormap,
1267                           gulong       pixel,
1268                           GdkColor    *result)
1269 {
1270   GdkVisual *visual;
1271
1272   g_return_if_fail (GDK_IS_COLORMAP (colormap));
1273   
1274   visual = gdk_colormap_get_visual (colormap);
1275
1276   switch (visual->type) {
1277   case GDK_VISUAL_DIRECT_COLOR:
1278   case GDK_VISUAL_TRUE_COLOR:
1279     result->red = 65535. * (double)((pixel & visual->red_mask) >> visual->red_shift) / ((1 << visual->red_prec) - 1);
1280     result->green = 65535. * (double)((pixel & visual->green_mask) >> visual->green_shift) / ((1 << visual->green_prec) - 1);
1281     result->blue = 65535. * (double)((pixel & visual->blue_mask) >> visual->blue_shift) / ((1 << visual->blue_prec) - 1);
1282     break;
1283   case GDK_VISUAL_STATIC_GRAY:
1284   case GDK_VISUAL_GRAYSCALE:
1285     result->red = result->green = result->blue = 65535. * (double)pixel/((1<<visual->depth) - 1);
1286     break;
1287   case GDK_VISUAL_STATIC_COLOR:
1288     g_assert_not_reached ();
1289     break;
1290   case GDK_VISUAL_PSEUDO_COLOR:
1291     result->red = colormap->colors[pixel].red;
1292     result->green = colormap->colors[pixel].green;
1293     result->blue = colormap->colors[pixel].blue;
1294     break;
1295   default:
1296     g_assert_not_reached ();
1297     break;
1298   }
1299 }
1300
1301 gboolean
1302 gdk_color_change (GdkColormap *colormap,
1303                   GdkColor    *color)
1304 {
1305   GdkColormapPrivateWin32 *private;
1306   XColor xcolor;
1307
1308   g_return_val_if_fail (GDK_IS_COLORMAP (colormap), FALSE);
1309   g_return_val_if_fail (color != NULL, FALSE);
1310
1311   private = GDK_COLORMAP_PRIVATE_DATA (colormap);
1312
1313   xcolor.peRed = color->red >> 8;
1314   xcolor.peGreen = color->green >> 8;
1315   xcolor.peBlue = color->blue >> 8;
1316
1317   if (SetPaletteEntries (private->xcolormap->palette,
1318                          color->pixel, 1, &xcolor) == 0)
1319     WIN32_GDI_FAILED ("SetPaletteEntries");
1320   private->xcolormap->stale = TRUE;
1321
1322   return TRUE;
1323 }
1324
1325 static gint
1326 gdk_colormap_match_color (GdkColormap *cmap,
1327                           GdkColor    *color,
1328                           const gchar *available)
1329 {
1330   GdkColor *colors;
1331   guint sum, max;
1332   gint rdiff, gdiff, bdiff;
1333   gint i, index;
1334
1335   g_return_val_if_fail (cmap != NULL, 0);
1336   g_return_val_if_fail (color != NULL, 0);
1337
1338   colors = cmap->colors;
1339   max = 3 * (65536);
1340   index = -1;
1341
1342   for (i = 0; i < cmap->size; i++)
1343     {
1344       if ((!available) || (available && available[i]))
1345         {
1346           rdiff = (color->red - colors[i].red);
1347           gdiff = (color->green - colors[i].green);
1348           bdiff = (color->blue - colors[i].blue);
1349
1350           sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
1351
1352           if (sum < max)
1353             {
1354               index = i;
1355               max = sum;
1356             }
1357         }
1358     }
1359
1360   return index;
1361 }
1362
1363 GdkColormap*
1364 gdk_colormap_lookup (Colormap xcolormap)
1365 {
1366   GdkColormap *cmap;
1367
1368   if (!colormap_hash)
1369     return NULL;
1370
1371   cmap = g_hash_table_lookup (colormap_hash, &xcolormap);
1372   return cmap;
1373 }
1374
1375 static void
1376 gdk_colormap_add (GdkColormap *cmap)
1377 {
1378   GdkColormapPrivateWin32 *private;
1379
1380   if (!colormap_hash)
1381     colormap_hash = g_hash_table_new ((GHashFunc) gdk_colormap_hash,
1382                                       (GEqualFunc) gdk_colormap_equal);
1383
1384   private = GDK_COLORMAP_PRIVATE_DATA (cmap);
1385
1386   g_hash_table_insert (colormap_hash, &private->xcolormap, cmap);
1387 }
1388
1389 static void
1390 gdk_colormap_remove (GdkColormap *cmap)
1391 {
1392   GdkColormapPrivateWin32 *private;
1393
1394   if (!colormap_hash)
1395     colormap_hash = g_hash_table_new ((GHashFunc) gdk_colormap_hash,
1396                                       (GEqualFunc) gdk_colormap_equal);
1397
1398   private = GDK_COLORMAP_PRIVATE_DATA (cmap);
1399
1400   g_hash_table_remove (colormap_hash, &private->xcolormap);
1401 }
1402
1403 static guint
1404 gdk_colormap_hash (Colormap *cmap)
1405 {
1406   return (guint) *cmap;
1407 }
1408
1409 static gboolean
1410 gdk_colormap_equal (Colormap *a,
1411                     Colormap *b)
1412 {
1413   return (*a == *b);
1414 }
1415
1416 #ifdef G_ENABLE_DEBUG
1417
1418 gchar *
1419 gdk_win32_color_to_string (const GdkColor *color)
1420 {
1421   static char buf[100];
1422
1423   sprintf (buf, "(%.04x,%.04x,%.04x):%.06x",
1424            color->red, color->green, color->blue, color->pixel);
1425
1426   return buf;
1427 }
1428
1429 #endif