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