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