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