]> Pileus Git - ~andy/gtk/blob - gdk/gdkcc.c
Added some extra functions to access the ColorContext toys.
[~andy/gtk] / gdk / gdkcc.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 /* Color Context module
20  * Copyright 1994,1995 John L. Cwikla
21  * Copyright (C) 1997 by Ripley Software Development
22  * Copyright (C) 1997 by Federico Mena (port to Gtk/Gdk)
23  */
24
25 /* Copyright 1994,1995 John L. Cwikla
26  *
27  * Permission to use, copy, modify, distribute, and sell this software
28  * and its documentation for any purpose is hereby granted without fee,
29  * provided that the above copyright notice appears in all copies and that
30  * both that copyright notice and this permission notice appear in
31  * supporting documentation, and that the name of John L. Cwikla or
32  * Wolfram Research, Inc not be used in advertising or publicity
33  * pertaining to distribution of the software without specific, written
34  * prior permission.  John L. Cwikla and Wolfram Research, Inc make no
35  * representations about the suitability of this software for any
36  * purpose.  It is provided "as is" without express or implied warranty.
37  *
38  * John L. Cwikla and Wolfram Research, Inc disclaim all warranties with
39  * regard to this software, including all implied warranties of
40  * merchantability and fitness, in no event shall John L. Cwikla or
41  * Wolfram Research, Inc be liable for any special, indirect or
42  * consequential damages or any damages whatsoever resulting from loss of
43  * use, data or profits, whether in an action of contract, negligence or
44  * other tortious action, arising out of or in connection with the use or
45  * performance of this software.
46  *
47  * Author:
48  *  John L. Cwikla
49  *  X Programmer
50  *  Wolfram Research Inc.
51  *
52  *  cwikla@wri.com
53  */
54
55 /* NOTES:
56  *
57  * - When a CC is destroyed, remember to destroy the hash table properly.
58  */
59
60
61 #include <X11/Xlib.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include "gdk.h"
65 #include "gdkprivate.h"
66 #include "gdkx.h"
67
68
69 #define MAX_IMAGE_COLORS 256
70
71
72 static guint
73 hash_color(gpointer key)
74 {
75         GdkColor *color = key;
76
77         return (color->red * 33023 + color->green * 30013 + color->blue * 27011);
78 }
79
80 static gint
81 compare_colors(gpointer a, gpointer b)
82 {
83         GdkColor *aa = a;
84         GdkColor *bb = b;
85
86         return ((aa->red == bb->red) && (aa->green == bb->green) && (aa->blue == bb->blue));
87 }
88
89 static void
90 free_hash_entry(gpointer key, gpointer value, gpointer user_data)
91 {
92         g_free(key); /* key and value are the same GdkColor */
93 }
94
95 static int
96 pixel_sort(const void *a, const void *b)
97 {
98         return ((GdkColor *) a)->pixel - ((GdkColor *) b)->pixel;
99 }
100
101 /* XXX: This function does an XQueryColors() the hard way, because there is
102  * no corresponding function in Gdk.
103  */
104
105 static void
106 my_x_query_colors(GdkColormap *colormap,
107                   GdkColor    *colors,
108                   gint         ncolors)
109 {
110         XColor *xcolors;
111         gint    i;
112
113         xcolors = g_new(XColor, ncolors);
114         for (i = 0; i < ncolors; i++)
115                 xcolors[i].pixel = colors[i].pixel;
116
117         XQueryColors(gdk_display, GDK_COLORMAP_XCOLORMAP(colormap), xcolors, ncolors);
118
119         for (i = 0; i < ncolors; i++) {
120                 colors[i].red   = xcolors[i].red;
121                 colors[i].green = xcolors[i].green;
122                 colors[i].blue  = xcolors[i].blue;
123         }
124
125         g_free(xcolors);
126 }
127
128 static void
129 query_colors(GdkColorContextPrivate *cc)
130 {
131         gint i;
132
133         cc->cmap = g_new(GdkColor, cc->num_colors);
134
135         for (i = 0; i < cc->num_colors; i++)
136                 cc->cmap[i].pixel = cc->clut ? cc->clut[i] : cc->std_cmap.base_pixel + i;
137
138         my_x_query_colors(cc->colormap, cc->cmap, cc->num_colors);
139         
140         qsort(cc->cmap, cc->num_colors, sizeof(GdkColor), pixel_sort);
141 }
142
143 static void
144 init_bw(GdkColorContextPrivate *cc)
145 {
146         GdkColor color;
147
148         g_warning("init_bw: failed to allocate colors, falling back to black and white");
149
150         cc->mode = GDK_CC_MODE_BW;
151
152         color.red = color.green = color.blue = 0;
153         if (!gdk_color_alloc(cc->colormap, &color))
154                 cc->black_pixel = 0;
155         else
156                 cc->black_pixel = color.pixel;
157
158         color.red = color.green = color.blue = 0xffff;
159         if (!gdk_color_alloc(cc->colormap, &color))
160                 cc->white_pixel = cc->black_pixel ? 0 : 1;
161         else
162                 cc->white_pixel = color.pixel;
163
164         cc->num_colors = 2;
165 }
166
167 static void
168 init_gray(GdkColorContextPrivate *cc)
169 {
170         GdkColor *clrs, *cstart;
171         gint i;
172         gdouble dinc;
173
174         cc->num_colors = GDK_VISUAL_XVISUAL(cc->visual)->map_entries;
175
176         cc->clut = g_new(gulong, cc->num_colors);
177         cstart = g_new(GdkColor, cc->num_colors);
178
179 retrygray:
180
181         dinc = 65535.0 / (cc->num_colors - 1);
182
183         clrs = cstart;
184
185         for (i = 0; i < cc->num_colors; i++) {
186                 clrs->red = clrs->green = clrs->blue = dinc * i;
187
188                 if (!gdk_color_alloc(cc->colormap, clrs)) {
189                         gdk_colors_free(cc->colormap, cc->clut, i, 0);
190
191                         cc->num_colors /= 2;
192
193                         if (cc->num_colors > 1)
194                                 goto retrygray;
195                         else {
196                                 g_free(cc->clut);
197                                 cc->clut = NULL;
198                                 init_bw(cc);
199                                 g_free(cstart);
200                                 return;
201                         }
202                 }
203
204                 cc->clut[i] = clrs++->pixel;
205         }
206
207         g_free(cstart);
208
209         /* XXX: is this the right thing to do? */
210         cc->std_cmap.colormap = GDK_COLORMAP_XCOLORMAP(cc->colormap);
211         cc->std_cmap.base_pixel = 0;
212         cc->std_cmap.red_max = cc->num_colors - 1;
213         cc->std_cmap.green_max = 0;
214         cc->std_cmap.blue_max = 0;
215         cc->std_cmap.red_mult = 1;
216         cc->std_cmap.green_mult = 0;
217         cc->std_cmap.blue_mult = 0;
218
219         cc->white_pixel = WhitePixel(cc->xdisplay, gdk_screen);
220         cc->black_pixel = BlackPixel(cc->xdisplay, gdk_screen);
221
222         query_colors(cc);
223
224         cc->mode = GDK_CC_MODE_MY_GRAY;
225 }
226
227 static void
228 init_color(GdkColorContextPrivate *cc)
229 {
230         gint cubeval;
231
232         cubeval = 1;
233         while ((cubeval * cubeval * cubeval) < GDK_VISUAL_XVISUAL(cc->visual)->map_entries)
234                 cubeval++;
235         cubeval--;
236
237         cc->num_colors = cubeval * cubeval * cubeval;
238
239         cc->std_cmap.red_max    = cubeval - 1;
240         cc->std_cmap.green_max  = cubeval - 1;
241         cc->std_cmap.blue_max   = cubeval - 1;
242         cc->std_cmap.red_mult   = cubeval * cubeval;
243         cc->std_cmap.green_mult = cubeval;
244         cc->std_cmap.blue_mult  = 1;
245         cc->std_cmap.base_pixel = 0;
246
247         cc->white_pixel = WhitePixel(cc->xdisplay, gdk_screen);
248         cc->black_pixel = BlackPixel(cc->xdisplay, gdk_screen);
249         cc->num_colors = DisplayCells(cc->xdisplay, gdk_screen);
250
251         /* a CLUT for storing allocated pixel indices */
252
253         cc->max_colors = cc->num_colors;
254         cc->clut = g_new(gulong, cc->max_colors);
255
256         for (cubeval = 0; cubeval < cc->max_colors; cubeval++)
257                 cc->clut[cubeval] = cubeval;
258
259         query_colors(cc);
260
261         cc->mode = GDK_CC_MODE_STD_CMAP;
262 }
263
264
265 static void
266 init_true_color(GdkColorContextPrivate *cc)
267 {
268         gulong rmask, gmask, bmask;
269
270         cc->mode = GDK_CC_MODE_TRUE;
271
272         /* Red */
273
274         rmask = cc->masks.red = cc->visual->red_mask;
275
276         cc->shifts.red = 0;
277         cc->bits.red = 0;
278
279         while (!(rmask & 1)) {
280                 rmask >>= 1;
281                 cc->shifts.red++;
282         }
283
284         while (rmask & 1) {
285                 rmask >>= 1;
286                 cc->bits.red++;
287         }
288
289         /* Green */
290
291         gmask = cc->masks.green = cc->visual->green_mask;
292
293         cc->shifts.green = 0;
294         cc->bits.green = 0;
295
296         while (!(gmask & 1)) {
297                 gmask >>= 1;
298                 cc->shifts.green++;
299         }
300
301         while (gmask & 1) {
302                 gmask >>= 1;
303                 cc->bits.green++;
304         }
305
306         /* Blue */
307
308         bmask = cc->masks.blue = cc->visual->blue_mask;
309
310         cc->shifts.blue = 0;
311         cc->bits.blue = 0;
312
313         while (!(bmask & 1)) {
314                 bmask >>= 1;
315                 cc->shifts.blue++;
316         }
317
318         while (bmask & 1) {
319                 bmask >>= 1;
320                 cc->bits.blue++;
321         }
322
323         cc->num_colors = (cc->visual->red_mask | cc->visual->green_mask | cc->visual->blue_mask) + 1;
324         cc->white_pixel = WhitePixel(cc->xdisplay, gdk_screen);
325         cc->black_pixel = BlackPixel(cc->xdisplay, gdk_screen);
326 }
327
328 static void
329 init_direct_color(GdkColorContextPrivate *cc)
330 {
331         gint n, count;
332         GdkColor *clrs, *cstart;
333         gulong rval, gval, bval;
334         gulong *rtable;
335         gulong *gtable;
336         gulong *btable;
337         gdouble dinc;
338
339         init_true_color(cc); /* for shift stuff */
340
341         rval = cc->visual->red_mask >> cc->shifts.red;
342         gval = cc->visual->green_mask >> cc->shifts.green;
343         bval = cc->visual->blue_mask >> cc->shifts.blue;
344
345         rtable = g_new(gulong, rval + 1);
346         gtable = g_new(gulong, gval + 1);
347         btable = g_new(gulong, bval + 1);
348
349         cc->max_entry = MAX(rval, gval);
350         cc->max_entry = MAX(cc->max_entry, bval);
351
352         cstart = g_new(GdkColor, cc->max_entry + 1);
353         cc->clut = g_new(gulong, cc->max_entry + 1);
354
355 retrydirect:
356
357         for (n = 0; n < rval; n++)
358                 rtable[n] = rval ? (65535.0 / rval * n) : 0;
359
360         for (n = 0; n < gval; n++)
361                 gtable[n] = gval ? (65535.0 / gval * n) : 0;
362
363         for (n = 0; n < bval; n++)
364                 btable[n] = bval ? (65535.0 / bval * n) : 0;
365
366         cc->max_entry = MAX(rval, gval);
367         cc->max_entry = MAX(cc->max_entry, bval);
368
369         count = 0;
370         clrs = cstart;
371         cc->num_colors = (rval + 1) * (gval + 1) * (bval + 1);
372
373         for (n = 0; n < cc->max_entry; n++) {
374                 dinc = (double) n / cc->max_entry;
375
376                 clrs->red   = rtable[(int) (dinc * rval)];
377                 clrs->green = gtable[(int) (dinc * gval)];
378                 clrs->blue  = btable[(int) (dinc * bval)];
379
380                 if (gdk_color_alloc(cc->colormap, clrs)) {
381                         cc->clut[count++] = clrs->pixel;
382                         clrs++;
383                 } else {
384                         gdk_colors_free(cc->colormap, cc->clut, count, 0);
385
386                         rval >>= 1;
387                         gval >>= 1;
388                         bval >>= 1;
389
390                         cc->masks.red   = (cc->masks.red >> 1) & cc->visual->red_mask;
391                         cc->masks.green = (cc->masks.green >> 1) & cc->visual->green_mask;
392                         cc->masks.blue  = (cc->masks.blue >> 1) & cc->visual->blue_mask;
393
394                         cc->shifts.red++;
395                         cc->shifts.green++;
396                         cc->shifts.blue++;
397
398                         cc->bits.red--;
399                         cc->bits.green--;
400                         cc->bits.blue--;
401
402                         cc->num_colors = (rval + 1) * (gval + 1) * (bval + 1);
403
404                         if (cc->num_colors >1)
405                                 goto retrydirect;
406                         else {
407                                 g_free(cc->clut);
408                                 cc->clut = NULL;
409                                 init_bw(cc);
410                                 break;
411                         }
412                 }
413         }
414
415         /* Update allocated color count; original num_colors is max_entry, which
416          * is not necessarily the same as the really allocated number of colors.
417          */
418
419         cc->num_colors = count;
420
421         g_free(rtable);
422         g_free(gtable);
423         g_free(btable);
424         g_free(cstart);
425 }
426
427 static void
428 init_palette(GdkColorContextPrivate *cc)
429 {
430         /* restore correct mode for this cc */
431         
432         switch (cc->visual->type) {
433                 case GDK_VISUAL_STATIC_GRAY:
434                 case GDK_VISUAL_GRAYSCALE:
435                         if (GDK_VISUAL_XVISUAL(cc->visual)->map_entries == 2)
436                                 cc->mode = GDK_CC_MODE_BW;
437                         else
438                                 cc->mode = GDK_CC_MODE_MY_GRAY;
439                         break;
440
441                 case GDK_VISUAL_TRUE_COLOR:
442                 case GDK_VISUAL_DIRECT_COLOR:
443                         cc->mode = GDK_CC_MODE_TRUE;
444                         break;
445
446                 case GDK_VISUAL_STATIC_COLOR:
447                 case GDK_VISUAL_PSEUDO_COLOR:
448                         cc->mode = GDK_CC_MODE_STD_CMAP;
449                         break;
450
451                 default:
452                         cc->mode = GDK_CC_MODE_UNDEFINED;
453                         break;
454         }
455
456         /* previous palette */
457
458         if (cc->num_palette)
459                 g_free(cc->palette);
460
461         if (cc->fast_dither)
462                 g_free(cc->fast_dither);
463
464         /* clear hash table if present */
465
466         if (cc->color_hash) {
467                 /* XXX: quick-and-dirty way to remove everything */
468
469                 g_hash_table_destroy(cc->color_hash);
470                 cc->color_hash = g_hash_table_new(hash_color, compare_colors);
471         }
472
473         cc->palette = NULL;
474         cc->num_palette = 0;
475         cc->fast_dither = NULL;
476 }
477
478 GdkColorContext *
479 gdk_color_context_new(GdkVisual   *visual,
480                       GdkColormap *colormap)
481 {
482         gint use_private_colormap = FALSE; /* XXX: maybe restore full functionality later? */
483         GdkColorContextPrivate *cc;
484         gint retry_count;
485         GdkColormap *default_colormap;
486
487         g_assert(visual != NULL);
488         g_assert(colormap != NULL);
489         
490         cc = g_new(GdkColorContextPrivate, 1);
491         
492         cc->xdisplay = gdk_display;
493         cc->visual = visual;
494         cc->colormap = colormap;
495         cc->clut = NULL;
496         cc->cmap = NULL;
497         cc->mode = GDK_CC_MODE_UNDEFINED;
498         cc->need_to_free_colormap = FALSE;
499
500         cc->color_hash = NULL;
501         cc->palette = NULL;
502         cc->num_palette = 0;
503         cc->fast_dither = NULL;
504
505         default_colormap = gdk_colormap_get_system();
506
507         retry_count = 0;
508
509         while (retry_count < 2) {
510                 /* Only create a private colormap if the visual found isn't equal
511                  * to the default visual and we don't have a private colormap,
512                  * -or- if we are instructed to create a private colormap (which
513                  * never is the case for XmHTML).
514                  */
515
516                 if (use_private_colormap
517                     || ((cc->visual != gdk_visual_get_system()) /* default visual? */
518                         && (GDK_COLORMAP_XCOLORMAP(colormap) == GDK_COLORMAP_XCOLORMAP(default_colormap)))) {
519                         g_warning("gdk_color_context_new: non-default visual detected, "
520                                   "using private colormap");
521
522                         cc->colormap = gdk_colormap_new(cc->visual, FALSE);
523
524                         cc->need_to_free_colormap = (GDK_COLORMAP_XCOLORMAP(colormap)
525                                                      != GDK_COLORMAP_XCOLORMAP(default_colormap));
526                 }
527
528                 switch (visual->type) {
529                         case GDK_VISUAL_STATIC_GRAY:
530                         case GDK_VISUAL_GRAYSCALE:
531                                 if (gdk_debug_level >= 1)
532                                         g_print("gdk_color_context_new: visual class is %s",
533                                                 (visual->type == GDK_VISUAL_STATIC_GRAY) ?
534                                                 "GDK_VISUAL_STATIC_GRAY" :
535                                                 "GDK_VISUAL_GRAYSCALE");
536
537                                 if (GDK_VISUAL_XVISUAL(cc->visual)->map_entries == 2)
538                                         init_bw(cc);
539                                 else
540                                         init_gray(cc);
541
542                                 break;
543
544                         case GDK_VISUAL_TRUE_COLOR: /* shifts */
545                                 if (gdk_debug_level >= 1)
546                                         g_print("gdk_color_context_new: visual class is "
547                                                  "GDK_VISUAL_TRUE_COLOR");
548
549                                 init_true_color(cc);
550                                 break;
551
552                         case GDK_VISUAL_DIRECT_COLOR: /* shifts and fake CLUT */
553                                 if (gdk_debug_level >= 1)
554                                         g_print("gdk_color_context_new: visual class is "
555                                                  "GDK_VISUAL_DIRECT_COLOR");
556
557                                 init_direct_color(cc);
558                                 break;
559
560                         case GDK_VISUAL_STATIC_COLOR:
561                         case GDK_VISUAL_PSEUDO_COLOR:
562                                 if (gdk_debug_level >= 1)
563                                         g_print("gdk_color_context_new: visual class is %s",
564                                                 (visual->type == GDK_VISUAL_STATIC_COLOR) ?
565                                                 "GDK_VISUAL_STATIC_COLOR" :
566                                                 "GDK_VISUAL_PSEUDO_COLOR");
567
568                                 init_color(cc);
569                                 break;
570
571                         default:
572                                 g_assert_not_reached();
573                 }
574
575                 if ((cc->mode == GDK_CC_MODE_BW) && (cc->visual->depth > 1)) {
576                         use_private_colormap = TRUE;
577                         retry_count++;
578                 } else
579                         break;
580         }
581
582         /* no. of colors allocated yet */
583
584         cc->num_allocated = 0;
585
586         if (gdk_debug_level >= 1)
587                 g_print("gdk_color_context_new: screen depth is %i, no. of colors is %i",
588                         cc->visual->depth, cc->num_colors);
589
590         /* check if we need to initialize a hash table */
591
592         if ((cc->mode == GDK_CC_MODE_STD_CMAP) || (cc->mode == GDK_CC_MODE_UNDEFINED))
593                 cc->color_hash = g_hash_table_new(hash_color, compare_colors);
594
595         return (GdkColorContext *) cc;
596 }
597
598 GdkColorContext *
599 gdk_color_context_new_mono(GdkVisual   *visual,
600                            GdkColormap *colormap)
601 {
602         GdkColorContextPrivate *cc;
603
604         g_assert(visual != NULL);
605         g_assert(colormap != NULL);
606
607         cc = g_new(GdkColorContextPrivate, 1);
608
609         cc->xdisplay = gdk_display;
610         cc->visual = visual;
611         cc->colormap = colormap;
612         cc->clut = NULL;
613         cc->cmap = NULL;
614         cc->mode = GDK_CC_MODE_UNDEFINED;
615         cc->need_to_free_colormap = FALSE;
616
617         init_bw(cc);
618
619         return (GdkColorContext *) cc;
620 }
621
622 /* This doesn't currently free black/white, hmm... */
623
624 void
625 gdk_color_context_free(GdkColorContext *cc)
626 {
627         GdkColorContextPrivate *ccp;
628         
629         g_assert(cc != NULL);
630
631         ccp = (GdkColorContextPrivate *) cc;
632
633         if ((ccp->visual->type == GDK_VISUAL_STATIC_COLOR)
634             || (ccp->visual->type == GDK_VISUAL_PSEUDO_COLOR)) {
635                 gdk_colors_free(ccp->colormap, ccp->clut, ccp->num_allocated, 0);
636                 g_free(ccp->clut);
637         } else if (ccp->clut != NULL) {
638                 gdk_colors_free(ccp->colormap, ccp->clut, ccp->num_colors, 0);
639                 g_free(ccp->clut);
640         }
641
642         if (ccp->cmap != NULL)
643                 g_free(ccp->cmap);
644
645         if (ccp->need_to_free_colormap)
646                 gdk_colormap_destroy(ccp->colormap);
647
648         /* free any palette that has been associated with this GdkColorContext */
649
650         init_palette(ccp);
651
652         if (ccp->color_hash) {
653                 g_hash_table_foreach(ccp->color_hash,
654                                      free_hash_entry,
655                                      NULL);
656                 g_hash_table_destroy(ccp->color_hash);
657         }
658
659         g_free(cc);
660 }
661
662 gulong
663 gdk_color_context_get_pixel(GdkColorContext *cc,
664                             gushort          red,
665                             gushort          green,
666                             gushort          blue,
667                             gint            *failed)
668 {
669         GdkColorContextPrivate *ccp;
670         
671         g_assert(cc != NULL);
672         g_assert(failed != NULL);
673
674         ccp = (GdkColorContextPrivate *) cc;
675
676         *failed = FALSE;
677
678         switch (ccp->mode) {
679                 case GDK_CC_MODE_BW: {
680                         gdouble value;
681
682                         red   <<= 8;
683                         green <<= 8;
684                         blue  <<= 8;
685
686                         value = red / 65535.0 * 0.30
687                                 + green / 65535.0 * 0.59
688                                 + blue / 65535.0 * 0.11;
689
690                         if (value > 0.5)
691                                 return ccp->white_pixel;
692
693                         return ccp->black_pixel;
694                 }
695
696                 case GDK_CC_MODE_MY_GRAY: {
697                         gulong ired, igreen, iblue;
698
699                         red   <<= 8;
700                         green <<= 8;
701                         blue  <<= 8;
702
703                         red   = red * 0.30 + green * 0.59 + blue * 0.11;
704                         green = 0;
705                         blue  = 0;
706
707                         if ((ired = red * (ccp->std_cmap.red_max + 1) / 0xffff)
708                             > ccp->std_cmap.red_max)
709                                 ired = ccp->std_cmap.red_max;
710
711                         ired *= ccp->std_cmap.red_mult;
712
713                         if ((igreen = green * (ccp->std_cmap.green_max + 1) / 0xffff)
714                             > ccp->std_cmap.green_max)
715                                 igreen = ccp->std_cmap.green_max;
716
717                         igreen *= ccp->std_cmap.green_mult;
718
719                         if ((iblue = blue * (ccp->std_cmap.blue_max + 1) / 0xffff)
720                             > ccp->std_cmap.blue_max)
721                                 iblue = ccp->std_cmap.blue_max;
722
723                         iblue *= ccp->std_cmap.blue_mult;
724
725                         if (ccp->clut != NULL)
726                                 return ccp->clut[ccp->std_cmap.base_pixel + ired + igreen + iblue];
727
728                         return ccp->std_cmap.base_pixel + ired + igreen + iblue;
729                 }
730
731                 case GDK_CC_MODE_TRUE: {
732                         gulong ired, igreen, iblue;
733
734                         red   <<= 8;
735                         green <<= 8;
736                         blue  <<= 8;
737
738                         if (ccp->clut == NULL) {
739                                 red   >>= 16 - ccp->bits.red;
740                                 green >>= 16 - ccp->bits.green;
741                                 blue  >>= 16 - ccp->bits.blue;
742
743                                 ired   = (red << ccp->shifts.red) & ccp->masks.red;
744                                 igreen = (green << ccp->shifts.green) & ccp->masks.green;
745                                 iblue  = (blue << ccp->shifts.blue) & ccp->masks.blue;
746
747                                 return ired | igreen | iblue;
748                         }
749
750                         ired   = ccp->clut[red * ccp->max_entry / 65535] & ccp->masks.red;
751                         igreen = ccp->clut[green * ccp->max_entry / 65535] & ccp->masks.green;
752                         iblue  = ccp->clut[blue * ccp->max_entry / 65535] & ccp->masks.blue;
753
754                         return ired | igreen | iblue;
755                 }
756
757                 case GDK_CC_MODE_PALETTE:
758                         return gdk_color_context_get_pixel_from_palette(cc, &red, &green, &blue, failed);
759
760                 case GDK_CC_MODE_STD_CMAP:
761                 default: {
762                         GdkColor color;
763                         GdkColor *result;
764
765                         red   <<= 8;
766                         green <<= 8;
767                         blue  <<= 8;
768
769                         color.red   = red;
770                         color.green = green;
771                         color.blue  = blue;
772
773                         result = g_hash_table_lookup(ccp->color_hash, &color);
774
775                         if (!result) {
776                                 color.red   = red;
777                                 color.green = green;
778                                 color.blue  = blue;
779                                 color.pixel = 0;
780
781                                 if (!gdk_color_alloc(ccp->colormap, &color))
782                                         *failed = TRUE;
783                                 else {
784                                         GdkColor *cnew;
785                                         
786                                         /* XXX: the following comment comes directly from
787                                          * XCC.c.  I don't know if it is relevant for
788                                          * gdk_color_alloc() as it is for XAllocColor()
789                                          * - Federico
790                                          */
791                                         /*
792                                          * I can't figure this out entirely, but it *is* possible
793                                          * that XAllocColor succeeds, even if the number of
794                                          * allocations we've made exceeds the number of available
795                                          * colors in the current colormap. And therefore it
796                                          * might be necessary for us to resize the CLUT.
797                                          */
798
799                                         if (ccp->num_allocated == ccp->max_colors) {
800                                                 ccp->max_colors *= 2;
801
802                                                 if (gdk_debug_level >= 1)
803                                                         g_print("gdk_color_context_get_pixel: "
804                                                                 "resizing CLUT to %i entries",
805                                                                 ccp->max_colors);
806
807                                                 ccp->clut = g_realloc(ccp->clut,
808                                                                       ccp->max_colors * sizeof(gulong));
809                                         }
810
811                                         /* Key and value are the same color structure */
812
813                                         cnew = g_new(GdkColor, 1);
814                                         *cnew = color;
815                                         g_hash_table_insert(ccp->color_hash, cnew, cnew);
816
817                                         ccp->clut[ccp->num_allocated] = color.pixel;
818                                         ccp->num_allocated++;
819                                         return color.pixel;
820                                 }
821                         }
822                         
823                         return result->pixel;
824                 }
825         }
826 }
827
828 void
829 gdk_color_context_get_pixels(GdkColorContext *cc,
830                              gushort         *reds,
831                              gushort         *greens,
832                              gushort         *blues,
833                              gint             ncolors,
834                              gulong          *colors,
835                              gint            *nallocated)
836 {
837         GdkColorContextPrivate *ccp;
838         gint i, k, idx;
839         gint cmapsize, ncols = 0, nopen = 0, counter = 0;
840         gint bad_alloc = FALSE;
841         gint failed[MAX_IMAGE_COLORS], allocated[MAX_IMAGE_COLORS];
842         GdkColor defs[MAX_IMAGE_COLORS], cmap[MAX_IMAGE_COLORS];
843         gint exact_col = 0, subst_col = 0, close_col = 0, black_col = 0;
844
845         g_assert(cc != NULL);
846         g_assert(reds != NULL);
847         g_assert(greens != NULL);
848         g_assert(blues != NULL);
849         g_assert(colors != NULL);
850         g_assert(nallocated != NULL);
851
852         ccp = (GdkColorContextPrivate *) cc;
853
854         memset(defs, 0, MAX_IMAGE_COLORS * sizeof(GdkColor));
855         memset(failed, 0, MAX_IMAGE_COLORS * sizeof(gint));
856         memset(allocated, 0, MAX_IMAGE_COLORS * sizeof(gint));
857
858         /* Will only have a value if used by the progressive image loader */
859
860         ncols = *nallocated;
861
862         *nallocated = 0;
863
864         /* First allocate all pixels */
865
866         for (i = 0; i < ncolors; i++) {
867                 /* colors[i] is only zero if the pixel at that location hasn't
868                  * been allocated yet.  This is a sanity check required for proper
869                  * color allocation by the progressive image loader
870                  */
871
872                 if (colors[i] == 0) {
873                         defs[i].red   = reds[i];
874                         defs[i].green = greens[i];
875                         defs[i].blue  = blues[i];
876
877                         colors[i] = gdk_color_context_get_pixel(cc, reds[i], greens[i], blues[i],
878                                                                 &bad_alloc);
879
880                         /* successfully allocated, store it */
881
882                         if (!bad_alloc) {
883                                 defs[i].pixel = colors[i];
884                                 allocated[ncols++] = colors[i];
885                         } else
886                                 failed[nopen++] = i;
887                 }
888         }
889
890         *nallocated = ncols;
891
892         /* all colors available, all done */
893
894         if ((ncols == ncolors) || (nopen == 0)) {
895                 if (gdk_debug_level >= 1)
896                         g_print("gdk_color_context_get_pixels: got all %i colors; "
897                                 "(%i colors allocated so far", ncolors, ccp->num_allocated);
898
899                 return;
900         }
901
902         /* The fun part.  We now try to allocate the colors we couldn't allocate
903          * directly.  The first step will map a color onto its nearest color
904          * that has been allocated (either by us or someone else).  If any colors
905          * remain unallocated, we map these onto the colors that we have allocated
906          * ourselves.
907          */
908
909         /* read up to MAX_IMAGE_COLORS colors of the current colormap */
910
911         cmapsize = MIN(ccp->num_colors, MAX_IMAGE_COLORS);
912
913         /* see if the colormap has any colors to read */
914
915         if (cmapsize < 0) {
916                 g_warning("gdk_color_context_get_pixels: oops!  no colors available, "
917                           "your images will look *really* ugly.");
918
919                 return;
920         }
921
922 #ifdef DEBUG
923         exact_col = ncols;
924 #endif
925
926         /* initialize pixels */
927
928         for (i = 0; i < cmapsize; i++) {
929                 cmap[i].pixel = i;
930                 cmap[i].red = cmap[i].green = cmap[i].blue = 0;
931         }
932
933         /* read the colormap */
934
935         my_x_query_colors(ccp->colormap, cmap, cmapsize);
936
937         /* speedup: downscale here instead of in the matching code */
938
939         for (i = 0; i < cmapsize; i++) {
940                 cmap[i].red   >>= 8;
941                 cmap[i].green >>= 8;
942                 cmap[i].blue  >>= 8;
943         }
944
945         /* get a close match for any unallocated colors */
946
947         counter = nopen;
948         nopen = 0;
949         idx = 0;
950
951         do {
952                 gint d, j, mdist, close, ri, gi, bi;
953                 gint rd, gd, bd;
954
955                 i = failed[idx];
956
957                 mdist = 1000000;
958                 close = -1;
959
960                 /* Store these vals.  Small performance increase as this skips three
961                  * indexing operations in the loop code.
962                  */
963
964                 ri = reds[i];
965                 gi = greens[i];
966                 bi = blues[i];
967
968                 /* Walk all colors in the colormap and see which one is the
969                  * closest.  Uses plain least squares.
970                  */
971
972                 for (j = 0; (j < cmapsize) && (mdist != 0); j++) {
973                         rd = ri - cmap[j].red;
974                         gd = gi - cmap[j].green;
975                         bd = bi - cmap[j].blue;
976
977                         d = rd * rd + gd * gd + bd * bd;
978
979                         if (d < mdist) {
980                                 close = j;
981                                 mdist = d;
982                         }
983                 }
984
985                 if (close != -1) {
986                         rd = cmap[close].red;
987                         gd = cmap[close].green;
988                         bd = cmap[close].blue;
989
990                         /* allocate */
991
992                         colors[i] = gdk_color_context_get_pixel(cc, rd, gd, bd, &bad_alloc);
993
994                         /* store */
995
996                         if (!bad_alloc) {
997                                 defs[i] = cmap[close];
998                                 defs[i].pixel = colors[i];
999                                 allocated[ncols++] = colors[i];
1000 #ifdef DEBUG
1001                                 close_col++;
1002 #endif
1003                         } else
1004                                 failed[nopen++] = i;
1005                 } else
1006                         failed[nopen++] = i;
1007                 /* deal with in next stage if allocation failed */
1008         } while (++idx < counter);
1009
1010         *nallocated = ncols;
1011
1012         /* This is the maximum no. of allocated colors.  See also the nopen == 0
1013          * note above.
1014          */
1015
1016         if ((ncols == ncolors) || (nopen == 0)) {
1017                 if (gdk_debug_level >= 1)
1018                         g_print("gdk_color_context_get_pixels: got %i colors, %i exact and "
1019                                 "%i close (%i colors allocated so far)",
1020                                 ncolors, exact_col, close_col, ccp->num_allocated);
1021
1022                 return;
1023         }
1024
1025         /* Now map any remaining unallocated pixels into the colors we did get */
1026
1027         idx = 0;
1028
1029         do {
1030                 gint d, mdist, close, ri, gi, bi;
1031                 gint j, rd, gd, bd;
1032
1033                 i = failed[idx];
1034
1035                 mdist = 1000000;
1036                 close = -1;
1037
1038                 /* store */
1039
1040                 ri = reds[i];
1041                 gi = greens[i];
1042                 bi = blues[i];
1043
1044                 /* search allocated colors */
1045
1046                 for (j = 0; (j < ncols) && (mdist != 0); j++) {
1047                         k = allocated[j];
1048
1049                         rd = ri - defs[k].red;
1050                         gd = gi - defs[k].green;
1051                         bd = bi - defs[k].blue;
1052
1053                         d = rd * rd + gd * gd + bd * bd;
1054
1055                         if (d < mdist) {
1056                                 close = k;
1057                                 mdist = d;
1058                         }
1059                 }
1060
1061                 if (close < 0) {
1062                         /* too bad, map to black */
1063
1064                         defs[i].pixel = ccp->black_pixel;
1065                         defs[i].red = defs[i].green = defs[i].blue = 0;
1066 #ifdef DEBUG
1067                         black_col++;
1068 #endif
1069                 } else {
1070                         defs[i] = defs[close];
1071 #ifdef DEBUG
1072                         subst_col++;
1073 #endif
1074                 }
1075
1076                 colors[i] = defs[i].pixel;
1077         } while (++idx < nopen);
1078
1079         if (gdk_debug_level >= 1)
1080                 g_print("gdk_color_context_get_pixels: got %i colors, %i exact, %i close, "
1081                         "%i substituted, %i to black (%i colors allocated so far)",
1082                         ncolors, exact_col, close_col, subst_col, black_col, ccp->num_allocated);
1083 }
1084
1085 void
1086 gdk_color_context_get_pixels_incremental(GdkColorContext *cc,
1087                                          gushort         *reds,
1088                                          gushort         *greens,
1089                                          gushort         *blues,
1090                                          gint             ncolors,
1091                                          gint            *used,
1092                                          gulong          *colors,
1093                                          gint            *nallocated)
1094 {
1095         GdkColorContextPrivate *ccp;
1096         gint i, k, idx;
1097         gint cmapsize, ncols = 0, nopen = 0, counter = 0;
1098         gint bad_alloc = FALSE;
1099         gint failed[MAX_IMAGE_COLORS], allocated[MAX_IMAGE_COLORS];
1100         GdkColor defs[MAX_IMAGE_COLORS], cmap[MAX_IMAGE_COLORS];
1101         gint exact_col = 0, subst_col = 0, close_col = 0, black_col = 0;
1102
1103         g_assert(cc != NULL);
1104         g_assert(reds != NULL);
1105         g_assert(greens != NULL);
1106         g_assert(blues != NULL);
1107         g_assert(used != NULL);
1108         g_assert(colors != NULL);
1109         g_assert(nallocated != NULL);
1110
1111         ccp = (GdkColorContextPrivate *) cc;
1112
1113         memset(defs, 0, MAX_IMAGE_COLORS * sizeof(GdkColor));
1114         memset(failed, 0, MAX_IMAGE_COLORS * sizeof(gint));
1115         memset(allocated, 0, MAX_IMAGE_COLORS * sizeof(gint));
1116
1117         /* Will only have a value if used by the progressive image loader */
1118
1119         ncols = *nallocated;
1120
1121         *nallocated = 0;
1122
1123         /* First allocate all pixels */
1124
1125         for (i = 0; i < ncolors; i++) {
1126                 /* used[i] is only -1 if the pixel at that location hasn't
1127                  * been allocated yet.  This is a sanity check required for proper
1128                  * color allocation by the progressive image loader.
1129                  * When colors[i] == 0 it indicates the slot is available for
1130                  * allocation.
1131                  */
1132
1133                 if (used[i] != FALSE) {
1134                         if (colors[i] == 0) {
1135                                 defs[i].red   = reds[i];
1136                                 defs[i].green = greens[i];
1137                                 defs[i].blue  = blues[i];
1138
1139                                 colors[i] = gdk_color_context_get_pixel(cc, reds[i], greens[i], blues[i], &bad_alloc);
1140
1141                                 /* successfully allocated, store it */
1142
1143                                 if (!bad_alloc) {
1144                                         defs[i].pixel = colors[i];
1145                                         allocated[ncols++] = colors[i];
1146                                 } else
1147                                         failed[nopen++] = i;
1148                         }
1149 #ifdef DEBUG
1150                         else
1151                                 if (gdk_debug_level >= 1)
1152                                         g_print("gdk_color_context_get_pixels_incremental: "
1153                                                 "pixel at slot %i already allocated, skipping", i);
1154 #endif
1155                 }
1156         }
1157
1158         *nallocated = ncols;
1159
1160         if ((ncols == ncolors) || (nopen == 0)) {
1161                 if (gdk_debug_level >= 1)
1162                         g_print("gdk_color_context_get_pixels_incremental: got all %i colors "
1163                                 "(%i colors allocated so far)",
1164                                 ncolors, ccp->num_allocated);
1165
1166                 return;
1167         }
1168
1169         cmapsize = MIN(ccp->num_colors, MAX_IMAGE_COLORS);
1170
1171         if (cmapsize < 0) {
1172                 g_warning("gdk_color_context_get_pixels_incremental: oops!  "
1173                           "No colors available images will look *really* ugly.");
1174                 return;
1175         }
1176
1177 #ifdef DEBUG
1178         exact_col = ncols;
1179 #endif
1180
1181         /* initialize pixels */
1182
1183         for (i = 0; i < cmapsize; i++) {
1184                 cmap[i].pixel = i;
1185                 cmap[i].red = cmap[i].green = cmap[i].blue = 0;
1186         }
1187
1188         /* read and downscale */
1189
1190         my_x_query_colors(ccp->colormap, cmap, cmapsize);
1191
1192         for (i = 0; i < cmapsize; i++) {
1193                 cmap[i].red   >>= 8;
1194                 cmap[i].green >>= 8;
1195                 cmap[i].blue  >>= 8;
1196         }
1197
1198         /* now match any unallocated colors */
1199
1200         counter = nopen;
1201         nopen = 0;
1202         idx = 0;
1203
1204         do {
1205                 gint d, j, mdist, close, ri, gi, bi;
1206                 gint rd, gd, bd;
1207
1208                 i = failed[idx];
1209
1210                 mdist = 1000000;
1211                 close = -1;
1212
1213                 /* store */
1214
1215                 ri = reds[i];
1216                 gi = greens[i];
1217                 bi = blues[i];
1218
1219                 for (j = 0; (j < cmapsize) && (mdist != 0); j++) {
1220                         rd = ri - cmap[j].red;
1221                         gd = gi - cmap[j].green;
1222                         bd = bi - cmap[j].blue;
1223
1224                         d = rd * rd + gd * gd + bd * bd;
1225
1226                         if (d < mdist) {
1227                                 close = j;
1228                                 mdist = d;
1229                         }
1230                 }
1231
1232                 if (close != -1) {
1233                         rd = cmap[close].red;
1234                         gd = cmap[close].green;
1235                         bd = cmap[close].blue;
1236
1237                         /* allocate */
1238
1239                         colors[i] = gdk_color_context_get_pixel(cc, rd, gd, bd, &bad_alloc);
1240
1241                         /* store */
1242
1243                         if (!bad_alloc) {
1244                                 defs[i] = cmap[close];
1245                                 defs[i].pixel = colors[i];
1246                                 allocated[ncols++] = colors[i];
1247 #ifdef DEBUG
1248                                 close_col++;
1249 #endif
1250                         } else
1251                                 failed[nopen++] = i;
1252                 } else
1253                         failed[nopen++] = i;
1254                 /* deal with in next stage if allocation failed */
1255         } while (++idx < counter);
1256
1257         *nallocated = ncols;
1258
1259         if ((ncols == ncolors) || (nopen == 0)) {
1260                 if (gdk_debug_level >= 1)
1261                         g_print("gdk_color_context_get_pixels_incremental: "
1262                                 "got %i colors, %i exact and %i close "
1263                                 "(%i colors allocated so far)",
1264                                 ncolors, exact_col, close_col, ccp->num_allocated);
1265
1266                 return;
1267         }
1268
1269         /* map remaining unallocated pixels into colors we did get */
1270
1271         idx = 0;
1272
1273         do {
1274                 gint d, mdist, close, ri, gi, bi;
1275                 gint j, rd, gd, bd;
1276
1277                 i = failed[idx];
1278
1279                 mdist = 1000000;
1280                 close = -1;
1281
1282                 ri = reds[i];
1283                 gi = greens[i];
1284                 bi = blues[i];
1285
1286                 /* search allocated colors */
1287
1288                 for (j = 0; (j < ncols) && (mdist != 0); j++) {
1289                         k = allocated[j];
1290
1291                         /* downscale */
1292
1293                         rd = ri - defs[k].red;
1294                         gd = gi - defs[k].green;
1295                         bd = bi - defs[k].blue;
1296
1297                         d = rd * rd + gd * gd + bd * bd;
1298
1299                         if (d < mdist) {
1300                                 close = k;
1301                                 mdist = d;
1302                         }
1303                 }
1304
1305                 if (close < 0) {
1306                         /* too bad, map to black */
1307
1308                         defs[i].pixel = ccp->black_pixel;
1309                         defs[i].red = defs[i].green = defs[i].blue = 0;
1310 #ifdef DEBUG
1311                         black_col++;
1312 #endif
1313                 } else {
1314                         defs[i] = defs[close];
1315 #ifdef DEBUG
1316                         subst_col++;
1317 #endif
1318                 }
1319
1320                 colors[i] = defs[i].pixel;
1321         } while (++idx < nopen);
1322
1323         if (gdk_debug_level >= 1)
1324                 g_print("gdk_color_context_get_pixels_incremental: "
1325                         "got %i colors, %i exact, %i close, %i substituted, %i to black "
1326                         "(%i colors allocated so far)",
1327                         ncolors, exact_col, close_col, subst_col, black_col, ccp->num_allocated);
1328 }
1329
1330 gint
1331 gdk_color_context_get_num_colors(GdkColorContext *cc)
1332 {
1333         g_assert(cc != NULL);
1334         
1335         return ((GdkColorContextPrivate *) cc)->num_colors;
1336 }
1337
1338 gint
1339 gdk_color_context_get_depth (GdkColorContext *cc)
1340 {
1341         GdkColorContextPrivate *ccp = (GdkColorContextPrivate *) cc;
1342
1343         g_assert (cc != NULL);
1344
1345         return ccp->visual->depth;
1346 }
1347
1348 GdkColorContextMode
1349 gdk_color_context_get_mode (GdkColorContext *cc)
1350 {
1351         g_assert (cc != NULL);
1352
1353         return ((GdkColorContextPrivate *) cc)->mode;
1354         
1355 }
1356
1357 gint
1358 gdk_color_context_query_color(GdkColorContext *cc,
1359                               GdkColor        *color)
1360 {
1361         return gdk_color_context_query_colors(cc, color, 1);
1362 }
1363
1364 gint
1365 gdk_color_context_query_colors(GdkColorContext *cc,
1366                                GdkColor        *colors,
1367                                gint             num_colors)
1368 {
1369         GdkColorContextPrivate *ccp;
1370         gint i;
1371         GdkColor *tc;
1372         
1373         g_assert(cc != NULL);
1374         g_assert(colors != NULL);
1375
1376         ccp = (GdkColorContextPrivate *) cc;
1377
1378         switch (ccp->mode) {
1379                 case GDK_CC_MODE_BW:
1380                         for (i = 0, tc = colors; i < num_colors; i++, tc++) {
1381                                 if (tc->pixel == ccp->white_pixel)
1382                                         tc->red = tc->green = tc->blue = 65535;
1383                                 else
1384                                         tc->red = tc->green = tc->blue = 0;
1385                         }
1386                         break;
1387
1388                 case GDK_CC_MODE_TRUE:
1389                         if (ccp->clut == NULL)
1390                                 for (i = 0, tc = colors; i < num_colors; i++, tc++) {
1391                                         tc->red   = (tc->pixel & ccp->masks.red) * 65535 / ccp->masks.red;
1392                                         tc->green = (tc->pixel & ccp->masks.green) * 65535 / ccp->masks.green;
1393                                         tc->blue  = (tc->pixel & ccp->masks.blue) * 65535 / ccp->masks.blue;
1394                                 }
1395                         else {
1396                                 my_x_query_colors(ccp->colormap, colors, num_colors);
1397                                 return 1;
1398                         }
1399                         break;
1400
1401                 case GDK_CC_MODE_STD_CMAP:
1402                 default:
1403                         if (ccp->cmap == NULL) {
1404                                 my_x_query_colors(ccp->colormap, colors, num_colors);
1405                                 return 1;
1406                         } else {
1407                                 gint first, last, half;
1408                                 gulong half_pixel;
1409
1410                                 for (i = 0, tc = colors; i < num_colors; i++) {
1411                                         first = 0;
1412                                         last = ccp->num_colors - 1;
1413
1414                                         while (first <= last) {
1415                                                 half = (first + last) / 2;
1416                                                 half_pixel = ccp->cmap[half].pixel;
1417
1418                                                 if (tc->pixel == half_pixel) {
1419                                                         tc->red   = ccp->cmap[half].red;
1420                                                         tc->green = ccp->cmap[half].green;
1421                                                         tc->blue  = ccp->cmap[half].blue;
1422                                                         first = last + 1; /* false break */
1423                                                 } else {
1424                                                         if (tc->pixel > half_pixel)
1425                                                                 first = half + 1;
1426                                                         else
1427                                                                 last = half - 1;
1428                                                 }
1429                                         }
1430                                 }
1431                                 return 1;
1432                         }
1433                         break;
1434         }
1435         return 1;
1436 }
1437
1438 gint
1439 gdk_color_context_add_palette(GdkColorContext *cc,
1440                               GdkColor        *palette,
1441                               gint             num_palette)
1442 {
1443         GdkColorContextPrivate *ccp;
1444         gint i, j, erg;
1445         gushort r, g, b;
1446         gulong pixel[1];
1447
1448         g_assert(cc != NULL);
1449
1450         ccp = (GdkColorContextPrivate *) cc;
1451
1452         /* initialize this palette (will also erase previous palette as well) */
1453
1454         init_palette(ccp);
1455
1456         /* restore previous mode if we aren't adding a new palette */
1457
1458         if (num_palette == 0) {
1459                 /* GDK_CC_MODE_STD_CMAP uses a hash table, so we'd better initialize one */
1460
1461                 /* XXX: here, the hash table is already initialized */
1462
1463                 return 0;
1464         }
1465
1466         /* Initialize a hash table for this palette (we need one for allocating
1467          * the pixels in the palette using the current settings)
1468          */
1469
1470         if (ccp->color_hash == NULL)
1471                 ccp->color_hash = g_hash_table_new(hash_color, compare_colors);
1472
1473         /* copy incoming palette */
1474
1475         ccp->palette = g_new0(GdkColor, num_palette);
1476
1477         j = 0;
1478
1479         for (i = 0; i < num_palette; i++) {
1480                 erg = 0;
1481                 pixel[0] = 0;
1482
1483                 /* try to allocate this color */
1484
1485                 r = palette[i].red;
1486                 g = palette[i].green;
1487                 b = palette[i].blue;
1488
1489                 gdk_color_context_get_pixels(cc, &r, &g, &b, 1, pixel, &erg);
1490
1491                 /* only store if we succeed */
1492
1493                 if (erg) {
1494                         /* store in palette */
1495
1496                         ccp->palette[j].red   = r;
1497                         ccp->palette[j].green = g;
1498                         ccp->palette[j].blue  = b;
1499                         ccp->palette[j].pixel = pixel[0];
1500
1501                         /* move to next slot */
1502
1503                         j++;
1504                 }
1505         }
1506
1507         /* resize to fit */
1508
1509         if (j != num_palette)
1510                 ccp->palette = g_realloc(ccp->palette, j * sizeof(GdkColor));
1511
1512         /* clear the hash table, we don't use it when dithering */
1513
1514         if (ccp->color_hash) {
1515                 g_hash_table_destroy(ccp->color_hash);
1516                 ccp->color_hash = NULL;
1517         }
1518
1519         /* store real palette size */
1520
1521         ccp->num_palette = j;
1522
1523         /* switch to palette mode */
1524
1525         ccp->mode = GDK_CC_MODE_PALETTE;
1526
1527         /* sort palette */
1528
1529         qsort(ccp->palette, ccp->num_palette, sizeof(GdkColor), pixel_sort);
1530
1531         ccp->fast_dither = NULL;
1532
1533         return j;
1534 }
1535
1536 void
1537 gdk_color_context_init_dither(GdkColorContext *cc)
1538 {
1539         GdkColorContextPrivate *ccp;
1540         gint rr, gg, bb, err, erg, erb;
1541         gint success = FALSE;
1542
1543         g_assert(cc != NULL);
1544
1545         ccp = (GdkColorContextPrivate *) cc;
1546
1547         /* now we can initialize the fast dither matrix */
1548
1549         if(ccp->fast_dither == NULL)
1550                 ccp->fast_dither = g_new(GdkColorContextDither, 1);
1551
1552         /* Fill it.  We ignore unsuccessful allocations, they are just mapped
1553          * to black instead */
1554
1555         for (rr = 0; rr < 32; rr++)
1556                 for (gg = 0; gg < 32; gg++)
1557                         for (bb = 0; bb < 32; bb++) {
1558                                 err = (rr << 3) | (rr >> 2);
1559                                 erg = (gg << 3) | (gg >> 2);
1560                                 erb = (bb << 3) | (bb >> 2);
1561
1562                                 ccp->fast_dither->fast_rgb[rr][gg][bb] =
1563                                         gdk_color_context_get_index_from_palette(cc, &err, &erg, &erb, &success);
1564                                 ccp->fast_dither->fast_err[rr][gg][bb] = err;
1565                                 ccp->fast_dither->fast_erg[rr][gg][bb] = erg;
1566                                 ccp->fast_dither->fast_erb[rr][gg][bb] = erb;
1567                         }
1568 }
1569
1570 void
1571 gdk_color_context_free_dither(GdkColorContext *cc)
1572 {
1573         GdkColorContextPrivate *ccp;
1574
1575         g_assert(cc != NULL);
1576
1577         ccp = (GdkColorContextPrivate *) cc;
1578
1579         if (ccp->fast_dither)
1580                 g_free(ccp->fast_dither);
1581
1582         ccp->fast_dither = NULL;
1583 }
1584
1585 gulong
1586 gdk_color_context_get_pixel_from_palette(GdkColorContext *cc,
1587                                          gushort         *red,
1588                                          gushort         *green,
1589                                          gushort         *blue,
1590                                          gint            *failed)
1591 {
1592         GdkColorContextPrivate *ccp;
1593         gulong pixel = 0;
1594         gint dif, dr, dg, db, j = -1;
1595         gint mindif = 0x7fffffff;
1596         gint err = 0, erg = 0, erb = 0;
1597         gint i;
1598
1599         g_assert(cc != NULL);
1600         g_assert(red != NULL);
1601         g_assert(green != NULL);
1602         g_assert(blue != NULL);
1603         g_assert(failed != NULL);
1604
1605         ccp = (GdkColorContextPrivate *) cc;
1606
1607         *failed = FALSE;
1608
1609         for (i = 0; i < ccp->num_palette; i++) {
1610                 dr = *red - ccp->palette[i].red;
1611                 dg = *green - ccp->palette[i].green;
1612                 db = *blue - ccp->palette[i].blue;
1613
1614                 dif = dr * dr + dg * dg + db * db;
1615                 
1616                 if (dif < mindif) {
1617                         mindif = dif;
1618                         j = i;
1619                         pixel = ccp->palette[i].pixel;
1620                         err = dr;
1621                         erg = dg;
1622                         erb = db;
1623
1624                         if (mindif == 0)
1625                                 break;
1626                 }
1627         }
1628
1629         /* we failed to map onto a color */
1630
1631         if (j == -1)
1632                 *failed = TRUE;
1633         else {
1634                 *red   = ABS(err);
1635                 *green = ABS(erg);
1636                 *blue  = ABS(erb);
1637         }
1638
1639         return pixel;
1640 }
1641
1642 guchar
1643 gdk_color_context_get_index_from_palette(GdkColorContext *cc,
1644                                          gint            *red,
1645                                          gint            *green,
1646                                          gint            *blue,
1647                                          gint            *failed)
1648 {
1649         GdkColorContextPrivate *ccp;
1650         gint dif, dr, dg, db, j = -1;
1651         gint mindif = 0x7fffffff;
1652         gint err = 0, erg = 0, erb = 0;
1653         gint i;
1654
1655         g_assert(cc != NULL);
1656         g_assert(red != NULL);
1657         g_assert(green != NULL);
1658         g_assert(blue != NULL);
1659         g_assert(failed != NULL);
1660
1661         ccp = (GdkColorContextPrivate *) cc;
1662
1663         *failed = FALSE;
1664
1665         for (i = 0; i < ccp->num_palette; i++) {
1666                 dr = *red - ccp->palette[i].red;
1667                 dg = *green - ccp->palette[i].green;
1668                 db = *blue - ccp->palette[i].blue;
1669
1670                 dif = dr * dr + dg * dg + db * db;
1671
1672                 if (dif < mindif) {
1673                         mindif = dif;
1674                         j = i;
1675                         err = dr;
1676                         erg = dg;
1677                         erb = db;
1678
1679                         if (mindif == 0)
1680                                 break;
1681                 }
1682         }
1683
1684         /* we failed to map onto a color */
1685
1686         if (j == -1) {
1687                 *failed = TRUE;
1688                 j = 0;
1689         } else {
1690                 /* return error fractions */
1691
1692                 *red   = err;
1693                 *green = erg;
1694                 *blue  = erb;
1695         }
1696
1697         return j;
1698 }