diff options
author | Arturo Espinosa <unammx@src.gnome.org> | 1997-12-17 00:14:36 +0000 |
---|---|---|
committer | Arturo Espinosa <unammx@src.gnome.org> | 1997-12-17 00:14:36 +0000 |
commit | 585dc6d78179c7eea1b62d5cd746d69e8aec4a32 (patch) | |
tree | f0f48dac72f730c93f25ba8df7e6b95bb06bdc08 /gdk | |
parent | ad5083714e45a676886b8289516db40bb0397056 (diff) | |
download | gtk+-585dc6d78179c7eea1b62d5cd746d69e8aec4a32.tar.gz |
New GdkColorContext object, ported from the XColorContext in XmHTML.
It compiles and links, but is *completely* untested. Feel free to
pound on it.
The idea is to do all color management (allocation, etc.) via a
GdkColorContext so that apps will be friendly to 8-bit displays.
GdkColorContext is supposed to work on all visual/depth combinations.
This support, however, is lacking from the rest of Gdk/Gtk. I will
try to work on that.
- Federico
Diffstat (limited to 'gdk')
-rw-r--r-- | gdk/Makefile.am | 1 | ||||
-rw-r--r-- | gdk/Makefile.in | 61 | ||||
-rw-r--r-- | gdk/gdk.h | 57 | ||||
-rw-r--r-- | gdk/gdkcc.c | 1679 | ||||
-rw-r--r-- | gdk/gdkprivate.h | 72 | ||||
-rw-r--r-- | gdk/gdktypes.h | 71 | ||||
-rw-r--r-- | gdk/x11/gdkcc-x11.c | 1679 |
7 files changed, 3558 insertions, 62 deletions
diff --git a/gdk/Makefile.am b/gdk/Makefile.am index 6956b30ae2..3e34aa4c23 100644 --- a/gdk/Makefile.am +++ b/gdk/Makefile.am @@ -6,6 +6,7 @@ lib_LTLIBRARIES = libgdk.la libgdk_la_SOURCES = \ gdk.c \ + gdkcc.c \ gdkcolor.c \ gdkcursor.c \ gdkdraw.c \ diff --git a/gdk/Makefile.in b/gdk/Makefile.in index a39837c3bc..70ab78a3b1 100644 --- a/gdk/Makefile.in +++ b/gdk/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated automatically by automake 1.2d from Makefile.am +# Makefile.in generated automatically by automake 1.2c from Makefile.am # Copyright (C) 1994, 1995, 1996, 1997 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation @@ -49,12 +49,12 @@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ transform = @program_transform_name@ -NORMAL_INSTALL = : -PRE_INSTALL = : -POST_INSTALL = : -NORMAL_UNINSTALL = : -PRE_UNINSTALL = : -POST_UNINSTALL = : +NORMAL_INSTALL = true +PRE_INSTALL = true +POST_INSTALL = true +NORMAL_UNINSTALL = true +PRE_UNINSTALL = true +POST_UNINSTALL = true host_alias = @host_alias@ host_triplet = @host@ CC = @CC@ @@ -78,6 +78,7 @@ lib_LTLIBRARIES = libgdk.la libgdk_la_SOURCES = \ gdk.c \ + gdkcc.c \ gdkcolor.c \ gdkcursor.c \ gdkdraw.c \ @@ -145,7 +146,7 @@ X_LIBS = @X_LIBS@ X_EXTRA_LIBS = @X_EXTRA_LIBS@ X_PRE_LIBS = @X_PRE_LIBS@ libgdk_la_LIBADD = -libgdk_la_OBJECTS = gdk.lo gdkcolor.lo gdkcursor.lo gdkdraw.lo \ +libgdk_la_OBJECTS = gdk.lo gdkcc.lo gdkcolor.lo gdkcursor.lo gdkdraw.lo \ gdkfont.lo gdkgc.lo gdkglobals.lo gdkimage.lo gdkinput.lo gdkpixmap.lo \ gdkproperty.lo gdkrectangle.lo gdkselection.lo gdkvisual.lo \ gdkwindow.lo gdkxid.lo gxid_lib.lo @@ -165,20 +166,21 @@ DIST_COMMON = Makefile.am Makefile.in DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) -TAR = gtar +TAR = tar GZIP = --best -DEP_FILES = .deps/gdk.P .deps/gdkcolor.P .deps/gdkcursor.P \ -.deps/gdkdraw.P .deps/gdkfont.P .deps/gdkgc.P .deps/gdkglobals.P \ -.deps/gdkimage.P .deps/gdkinput.P .deps/gdkpixmap.P .deps/gdkproperty.P \ -.deps/gdkrectangle.P .deps/gdkselection.P .deps/gdkvisual.P \ -.deps/gdkwindow.P .deps/gdkxid.P .deps/gxid.P .deps/gxid_lib.P +DEP_FILES = .deps/gdk.P .deps/gdkcc.P .deps/gdkcolor.P \ +.deps/gdkcursor.P .deps/gdkdraw.P .deps/gdkfont.P .deps/gdkgc.P \ +.deps/gdkglobals.P .deps/gdkimage.P .deps/gdkinput.P .deps/gdkpixmap.P \ +.deps/gdkproperty.P .deps/gdkrectangle.P .deps/gdkselection.P \ +.deps/gdkvisual.P .deps/gdkwindow.P .deps/gdkxid.P .deps/gxid.P \ +.deps/gxid_lib.P SOURCES = $(libgdk_la_SOURCES) $(gxid_SOURCES) OBJECTS = $(libgdk_la_OBJECTS) $(gxid_OBJECTS) default: all .SUFFIXES: -.SUFFIXES: .S .c .lo .o .s +.SUFFIXES: .c .lo .o $(srcdir)/Makefile.in: @MAINT@ Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) cd $(top_srcdir) && $(AUTOMAKE) --gnu gdk/Makefile @@ -201,8 +203,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES) $(mkinstalldirs) $(libdir) @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ if test -f $$p; then \ - echo "$(LIBTOOL) --mode=install $(INSTALL_DATA) $$p $(libdir)/$$p"; \ - $(LIBTOOL) --mode=install $(INSTALL_DATA) $$p $(libdir)/$$p; \ + echo "$(LIBTOOL) --mode=install $(INSTALL_PROGRAM) $$p $(libdir)/$$p"; \ + $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) $$p $(libdir)/$$p; \ else :; fi; \ done @@ -215,12 +217,6 @@ uninstall-libLTLIBRARIES: .c.o: $(COMPILE) -c $< -.s.o: - $(COMPILE) -c $< - -.S.o: - $(COMPILE) -c $< - mostlyclean-compile: -rm -f *.o core @@ -234,17 +230,11 @@ maintainer-clean-compile: .c.lo: $(LIBTOOL) --mode=compile $(COMPILE) -c $< -.s.lo: - $(LIBTOOL) --mode=compile $(COMPILE) -c $< - -.S.lo: - $(LIBTOOL) --mode=compile $(COMPILE) -c $< - mostlyclean-libtool: -rm -f *.lo clean-libtool: - -rm -rf .libs _libs + -rm -rf .libs distclean-libtool: @@ -298,15 +288,14 @@ uninstall-gdkincludeHEADERS: tags: TAGS -ID: $(HEADERS) $(SOURCES) $(LISP) - here=`pwd` && cd $(srcdir) \ - && mkid -f$$here/ID $(SOURCES) $(HEADERS) $(LISP) +ID: $(HEADERS) $(SOURCES) + here=`pwd` && cd $(srcdir) && mkid -f$$here/ID $(SOURCES) $(HEADERS) -TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) tags=; \ here=`pwd`; \ - test -z "$(ETAGS_ARGS)$(SOURCES)$(HEADERS)$(LISP)$$tags" \ - || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $(SOURCES) $(HEADERS) $(LISP) -o $$here/TAGS) + test -z "$(ETAGS_ARGS)$(SOURCES)$(HEADERS)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $(SOURCES) $(HEADERS) -o $$here/TAGS) mostlyclean-tags: @@ -648,6 +648,63 @@ GdkEventMask gdk_ic_get_events (GdkIC ic); /* Miscellaneous */ void gdk_event_send_clientmessage_toall(GdkEvent *event); +/* Color Context */ + +GdkColorContext *gdk_color_context_new (GdkVisual *visual, + GdkColormap *colormap); + +GdkColorContext *gdk_color_context_new_mono (GdkVisual *visual, + GdkColormap *colormap); + +void gdk_color_context_free (GdkColorContext *cc); + +gulong gdk_color_context_get_pixel (GdkColorContext *cc, + gushort red, + gushort green, + gushort blue, + gint *failed); +void gdk_color_context_get_pixels (GdkColorContext *cc, + gushort *reds, + gushort *greens, + gushort *blues, + gint ncolors, + gulong *colors, + gint *nallocated); +void gdk_color_context_get_pixels_incremental (GdkColorContext *cc, + gushort *reds, + gushort *greens, + gushort *blues, + gint ncolors, + gint *used, + gulong *colors, + gint *nallocated); + +gint gdk_color_context_get_num_colors (GdkColorContext *cc); +gint gdk_color_context_query_color (GdkColorContext *cc, + GdkColor *color); +gint gdk_color_context_query_colors (GdkColorContext *cc, + GdkColor *colors, + gint num_colors); + +gint gdk_color_context_add_palette (GdkColorContext *cc, + GdkColor *palette, + gint num_palette); + +void gdk_color_context_init_dither (GdkColorContext *cc); +void gdk_color_context_free_dither (GdkColorContext *cc); + +gulong gdk_color_context_get_pixel_from_palette (GdkColorContext *cc, + gushort *red, + gushort *green, + gushort *blue, + gint *failed); +guchar gdk_color_context_get_index_from_palette (GdkColorContext *cc, + gint *red, + gint *green, + gint *blue, + gint *failed); + + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/gdk/gdkcc.c b/gdk/gdkcc.c new file mode 100644 index 0000000000..0b089208d2 --- /dev/null +++ b/gdk/gdkcc.c @@ -0,0 +1,1679 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Color Context module + * Copyright 1994,1995 John L. Cwikla + * Copyright (C) 1997 by Ripley Software Development + * Copyright (C) 1997 by Federico Mena (port to Gtk/Gdk) + */ + +/* Copyright 1994,1995 John L. Cwikla + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of John L. Cwikla or + * Wolfram Research, Inc not be used in advertising or publicity + * pertaining to distribution of the software without specific, written + * prior permission. John L. Cwikla and Wolfram Research, Inc make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * John L. Cwikla and Wolfram Research, Inc disclaim all warranties with + * regard to this software, including all implied warranties of + * merchantability and fitness, in no event shall John L. Cwikla or + * Wolfram Research, Inc be liable for any special, indirect or + * consequential damages or any damages whatsoever resulting from loss of + * use, data or profits, whether in an action of contract, negligence or + * other tortious action, arising out of or in connection with the use or + * performance of this software. + * + * Author: + * John L. Cwikla + * X Programmer + * Wolfram Research Inc. + * + * cwikla@wri.com + */ + +/* NOTES: + * + * - When a CC is destroyed, remember to destroy the hash table properly. + */ + + +#include <X11/Xlib.h> +#include <stdlib.h> +#include <string.h> +#include "gdk.h" +#include "gdkprivate.h" +#include "gdkx.h" + + +#define MAX_IMAGE_COLORS 256 + + +static guint +hash_color(gpointer key) +{ + GdkColor *color = key; + + return (color->red * 33023 + color->green * 30013 + color->blue * 27011); +} + +static gint +compare_colors(gpointer a, gpointer b) +{ + GdkColor *aa = a; + GdkColor *bb = b; + + return ((aa->red == bb->red) && (aa->green == bb->green) && (aa->blue == bb->blue)); +} + +static void +free_hash_entry(gpointer key, gpointer value, gpointer user_data) +{ + g_free(key); /* key and value are the same GdkColor */ +} + +static int +pixel_sort(const void *a, const void *b) +{ + return ((GdkColor *) a)->pixel - ((GdkColor *) b)->pixel; +} + +/* XXX: This function does an XQueryColors() the hard way, because there is + * no corresponding function in Gdk. + */ + +static void +my_x_query_colors(GdkColormap *colormap, + GdkColor *colors, + gint ncolors) +{ + XColor *xcolors; + gint i; + + xcolors = g_new(XColor, ncolors); + for (i = 0; i < ncolors; i++) + xcolors[i].pixel = colors[i].pixel; + + XQueryColors(gdk_display, GDK_COLORMAP_XCOLORMAP(colormap), xcolors, ncolors); + + for (i = 0; i < ncolors; i++) { + colors[i].red = xcolors[i].red; + colors[i].green = xcolors[i].green; + colors[i].blue = xcolors[i].blue; + } + + g_free(xcolors); +} + +static void +query_colors(GdkColorContextPrivate *cc) +{ + gint i; + + cc->cmap = g_new(GdkColor, cc->num_colors); + + for (i = 0; i < cc->num_colors; i++) + cc->cmap[i].pixel = cc->clut ? cc->clut[i] : cc->std_cmap.base_pixel + i; + + my_x_query_colors(cc->colormap, cc->cmap, cc->num_colors); + + qsort(cc->cmap, cc->num_colors, sizeof(GdkColor), pixel_sort); +} + +static void +init_bw(GdkColorContextPrivate *cc) +{ + GdkColor color; + + g_warning("init_bw: failed to allocate colors, falling back to black and white"); + + cc->mode = GDK_CC_MODE_BW; + + color.red = color.green = color.blue = 0; + if (!gdk_color_alloc(cc->colormap, &color)) + cc->black_pixel = 0; + else + cc->black_pixel = color.pixel; + + color.red = color.green = color.blue = 0xffff; + if (!gdk_color_alloc(cc->colormap, &color)) + cc->white_pixel = cc->black_pixel ? 0 : 1; + else + cc->white_pixel = color.pixel; + + cc->num_colors = 2; +} + +static void +init_gray(GdkColorContextPrivate *cc) +{ + GdkColor *clrs, *cstart; + gint i; + gdouble dinc; + + cc->num_colors = GDK_VISUAL_XVISUAL(cc->visual)->map_entries; + + cc->clut = g_new(gulong, cc->num_colors); + cstart = g_new(GdkColor, cc->num_colors); + +retrygray: + + dinc = 65535.0 / (cc->num_colors - 1); + + clrs = cstart; + + for (i = 0; i < cc->num_colors; i++) { + clrs->red = clrs->green = clrs->blue = dinc * i; + + if (!gdk_color_alloc(cc->colormap, clrs)) { + gdk_colors_free(cc->colormap, cc->clut, i, 0); + + cc->num_colors /= 2; + + if (cc->num_colors > 1) + goto retrygray; + else { + g_free(cc->clut); + cc->clut = NULL; + init_bw(cc); + g_free(cstart); + return; + } + } + + cc->clut[i] = clrs++->pixel; + } + + g_free(cstart); + + /* XXX: is this the right thing to do? */ + cc->std_cmap.colormap = GDK_COLORMAP_XCOLORMAP(cc->colormap); + cc->std_cmap.base_pixel = 0; + cc->std_cmap.red_max = cc->num_colors - 1; + cc->std_cmap.green_max = 0; + cc->std_cmap.blue_max = 0; + cc->std_cmap.red_mult = 1; + cc->std_cmap.green_mult = 0; + cc->std_cmap.blue_mult = 0; + + cc->white_pixel = WhitePixel(cc->xdisplay, gdk_screen); + cc->black_pixel = BlackPixel(cc->xdisplay, gdk_screen); + + query_colors(cc); + + cc->mode = GDK_CC_MODE_MY_GRAY; +} + +static void +init_color(GdkColorContextPrivate *cc) +{ + gint cubeval; + + cubeval = 1; + while ((cubeval * cubeval * cubeval) < GDK_VISUAL_XVISUAL(cc->visual)->map_entries) + cubeval++; + cubeval--; + + cc->num_colors = cubeval * cubeval * cubeval; + + cc->std_cmap.red_max = cubeval - 1; + cc->std_cmap.green_max = cubeval - 1; + cc->std_cmap.blue_max = cubeval - 1; + cc->std_cmap.red_mult = cubeval * cubeval; + cc->std_cmap.green_mult = cubeval; + cc->std_cmap.blue_mult = 1; + cc->std_cmap.base_pixel = 0; + + cc->white_pixel = WhitePixel(cc->xdisplay, gdk_screen); + cc->black_pixel = BlackPixel(cc->xdisplay, gdk_screen); + cc->num_colors = DisplayCells(cc->xdisplay, gdk_screen); + + /* a CLUT for storing allocated pixel indices */ + + cc->max_colors = cc->num_colors; + cc->clut = g_new(gulong, cc->max_colors); + + for (cubeval = 0; cubeval < cc->max_colors; cubeval++) + cc->clut[cubeval] = cubeval; + + query_colors(cc); + + cc->mode = GDK_CC_MODE_STD_CMAP; +} + + +static void +init_true_color(GdkColorContextPrivate *cc) +{ + gulong rmask, gmask, bmask; + + cc->mode = GDK_CC_MODE_TRUE; + + /* Red */ + + rmask = cc->masks.red = cc->visual->red_mask; + + cc->shifts.red = 0; + cc->bits.red = 0; + + while (!(rmask & 1)) { + rmask >>= 1; + cc->shifts.red++; + } + + while (rmask & 1) { + rmask >>= 1; + cc->bits.red++; + } + + /* Green */ + + gmask = cc->masks.green = cc->visual->green_mask; + + cc->shifts.green = 0; + cc->bits.green = 0; + + while (!(gmask & 1)) { + gmask >>= 1; + cc->shifts.green++; + } + + while (gmask & 1) { + gmask >>= 1; + cc->bits.green++; + } + + /* Blue */ + + bmask = cc->masks.blue = cc->visual->blue_mask; + + cc->shifts.blue = 0; + cc->bits.blue = 0; + + while (!(bmask & 1)) { + bmask >>= 1; + cc->shifts.blue++; + } + + while (bmask & 1) { + bmask >>= 1; + cc->bits.blue++; + } + + cc->num_colors = (cc->visual->red_mask | cc->visual->green_mask | cc->visual->blue_mask) + 1; + cc->white_pixel = WhitePixel(cc->xdisplay, gdk_screen); + cc->black_pixel = BlackPixel(cc->xdisplay, gdk_screen); +} + +static void +init_direct_color(GdkColorContextPrivate *cc) +{ + gint n, count; + GdkColor *clrs, *cstart; + gulong rval, gval, bval; + gulong *rtable; + gulong *gtable; + gulong *btable; + gdouble dinc; + + init_true_color(cc); /* for shift stuff */ + + rval = cc->visual->red_mask >> cc->shifts.red; + gval = cc->visual->green_mask >> cc->shifts.green; + bval = cc->visual->blue_mask >> cc->shifts.blue; + + rtable = g_new(gulong, rval + 1); + gtable = g_new(gulong, gval + 1); + btable = g_new(gulong, bval + 1); + + cc->max_entry = MAX(rval, gval); + cc->max_entry = MAX(cc->max_entry, bval); + + cstart = g_new(GdkColor, cc->max_entry + 1); + cc->clut = g_new(gulong, cc->max_entry + 1); + +retrydirect: + + for (n = 0; n < rval; n++) + rtable[n] = rval ? (65535.0 / rval * n) : 0; + + for (n = 0; n < gval; n++) + gtable[n] = gval ? (65535.0 / gval * n) : 0; + + for (n = 0; n < bval; n++) + btable[n] = bval ? (65535.0 / bval * n) : 0; + + cc->max_entry = MAX(rval, gval); + cc->max_entry = MAX(cc->max_entry, bval); + + count = 0; + clrs = cstart; + cc->num_colors = (rval + 1) * (gval + 1) * (bval + 1); + + for (n = 0; n < cc->max_entry; n++) { + dinc = (double) n / cc->max_entry; + + clrs->red = rtable[(int) (dinc * rval)]; + clrs->green = gtable[(int) (dinc * gval)]; + clrs->blue = btable[(int) (dinc * bval)]; + + if (gdk_color_alloc(cc->colormap, clrs)) { + cc->clut[count++] = clrs->pixel; + clrs++; + } else { + gdk_colors_free(cc->colormap, cc->clut, count, 0); + + rval >>= 1; + gval >>= 1; + bval >>= 1; + + cc->masks.red = (cc->masks.red >> 1) & cc->visual->red_mask; + cc->masks.green = (cc->masks.green >> 1) & cc->visual->green_mask; + cc->masks.blue = (cc->masks.blue >> 1) & cc->visual->blue_mask; + + cc->shifts.red++; + cc->shifts.green++; + cc->shifts.blue++; + + cc->bits.red--; + cc->bits.green--; + cc->bits.blue--; + + cc->num_colors = (rval + 1) * (gval + 1) * (bval + 1); + + if (cc->num_colors >1) + goto retrydirect; + else { + g_free(cc->clut); + cc->clut = NULL; + init_bw(cc); + break; + } + } + } + + /* Update allocated color count; original num_colors is max_entry, which + * is not necessarily the same as the really allocated number of colors. + */ + + cc->num_colors = count; + + g_free(rtable); + g_free(gtable); + g_free(btable); + g_free(cstart); +} + +static void +init_palette(GdkColorContextPrivate *cc) +{ + /* restore correct mode for this cc */ + + switch (cc->visual->type) { + case GDK_VISUAL_STATIC_GRAY: + case GDK_VISUAL_GRAYSCALE: + if (GDK_VISUAL_XVISUAL(cc->visual)->map_entries == 2) + cc->mode = GDK_CC_MODE_BW; + else + cc->mode = GDK_CC_MODE_MY_GRAY; + break; + + case GDK_VISUAL_TRUE_COLOR: + case GDK_VISUAL_DIRECT_COLOR: + cc->mode = GDK_CC_MODE_TRUE; + break; + + case GDK_VISUAL_STATIC_COLOR: + case GDK_VISUAL_PSEUDO_COLOR: + cc->mode = GDK_CC_MODE_STD_CMAP; + break; + + default: + cc->mode = GDK_CC_MODE_UNDEFINED; + break; + } + + /* previous palette */ + + if (cc->num_palette) + g_free(cc->palette); + + if (cc->fast_dither) + g_free(cc->fast_dither); + + /* clear hash table if present */ + + if (cc->color_hash) { + /* XXX: quick-and-dirty way to remove everything */ + + g_hash_table_destroy(cc->color_hash); + cc->color_hash = g_hash_table_new(hash_color, compare_colors); + } + + cc->palette = NULL; + cc->num_palette = 0; + cc->fast_dither = NULL; +} + +GdkColorContext * +gdk_color_context_new(GdkVisual *visual, + GdkColormap *colormap) +{ + gint use_private_colormap = FALSE; /* XXX: maybe restore full functionality later? */ + GdkColorContextPrivate *cc; + gint retry_count; + GdkColormap *default_colormap; + + g_assert(visual != NULL); + g_assert(colormap != NULL); + + cc = g_new(GdkColorContextPrivate, 1); + + cc->xdisplay = gdk_display; + cc->visual = visual; + cc->colormap = colormap; + cc->clut = NULL; + cc->cmap = NULL; + cc->mode = GDK_CC_MODE_UNDEFINED; + cc->need_to_free_colormap = FALSE; + + cc->color_hash = NULL; + cc->palette = NULL; + cc->num_palette = 0; + cc->fast_dither = NULL; + + default_colormap = gdk_colormap_get_system(); + + retry_count = 0; + + while (retry_count < 2) { + /* Only create a private colormap if the visual found isn't equal + * to the default visual and we don't have a private colormap, + * -or- if we are instructed to create a private colormap (which + * never is the case for XmHTML). + */ + + if (use_private_colormap + || ((cc->visual != gdk_visual_get_system()) /* default visual? */ + && (GDK_COLORMAP_XCOLORMAP(colormap) == GDK_COLORMAP_XCOLORMAP(default_colormap)))) { + g_warning("gdk_color_context_new: non-default visual detected, " + "using private colormap"); + + cc->colormap = gdk_colormap_new(cc->visual, FALSE); + + cc->need_to_free_colormap = (GDK_COLORMAP_XCOLORMAP(colormap) + != GDK_COLORMAP_XCOLORMAP(default_colormap)); + } + + switch (visual->type) { + case GDK_VISUAL_STATIC_GRAY: + case GDK_VISUAL_GRAYSCALE: + if (gdk_debug_level >= 1) + g_print("gdk_color_context_new: visual class is %s", + (visual->type == GDK_VISUAL_STATIC_GRAY) ? + "GDK_VISUAL_STATIC_GRAY" : + "GDK_VISUAL_GRAYSCALE"); + + if (GDK_VISUAL_XVISUAL(cc->visual)->map_entries == 2) + init_bw(cc); + else + init_gray(cc); + + break; + + case GDK_VISUAL_TRUE_COLOR: /* shifts */ + if (gdk_debug_level >= 1) + g_print("gdk_color_context_new: visual class is " + "GDK_VISUAL_TRUE_COLOR"); + + init_true_color(cc); + break; + + case GDK_VISUAL_DIRECT_COLOR: /* shifts and fake CLUT */ + if (gdk_debug_level >= 1) + g_print("gdk_color_context_new: visual class is " + "GDK_VISUAL_DIRECT_COLOR"); + + init_direct_color(cc); + break; + + case GDK_VISUAL_STATIC_COLOR: + case GDK_VISUAL_PSEUDO_COLOR: + if (gdk_debug_level >= 1) + g_print("gdk_color_context_new: visual class is %s", + (visual->type == GDK_VISUAL_STATIC_COLOR) ? + "GDK_VISUAL_STATIC_COLOR" : + "GDK_VISUAL_PSEUDO_COLOR"); + + init_color(cc); + break; + + default: + g_assert_not_reached(); + } + + if ((cc->mode == GDK_CC_MODE_BW) && (cc->visual->depth > 1)) { + use_private_colormap = TRUE; + retry_count++; + } else + break; + } + + /* no. of colors allocated yet */ + + cc->num_allocated = 0; + + if (gdk_debug_level >= 1) + g_print("gdk_color_context_new: screen depth is %i, no. of colors is %i", + cc->visual->depth, cc->num_colors); + + /* check if we need to initialize a hash table */ + + if ((cc->mode == GDK_CC_MODE_STD_CMAP) || (cc->mode == GDK_CC_MODE_UNDEFINED)) + cc->color_hash = g_hash_table_new(hash_color, compare_colors); + + return (GdkColorContext *) cc; +} + +GdkColorContext * +gdk_color_context_new_mono(GdkVisual *visual, + GdkColormap *colormap) +{ + GdkColorContextPrivate *cc; + + g_assert(visual != NULL); + g_assert(colormap != NULL); + + cc = g_new(GdkColorContextPrivate, 1); + + cc->xdisplay = gdk_display; + cc->visual = visual; + cc->colormap = colormap; + cc->clut = NULL; + cc->cmap = NULL; + cc->mode = GDK_CC_MODE_UNDEFINED; + cc->need_to_free_colormap = FALSE; + + init_bw(cc); + + return (GdkColorContext *) cc; +} + +/* This doesn't currently free black/white, hmm... */ + +void +gdk_color_context_free(GdkColorContext *cc) +{ + GdkColorContextPrivate *ccp; + + g_assert(cc != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + if ((ccp->visual->type == GDK_VISUAL_STATIC_COLOR) + || (ccp->visual->type == GDK_VISUAL_PSEUDO_COLOR)) { + gdk_colors_free(ccp->colormap, ccp->clut, ccp->num_allocated, 0); + g_free(ccp->clut); + } else if (ccp->clut != NULL) { + gdk_colors_free(ccp->colormap, ccp->clut, ccp->num_colors, 0); + g_free(ccp->clut); + } + + if (ccp->cmap != NULL) + g_free(ccp->cmap); + + if (ccp->need_to_free_colormap) + gdk_colormap_destroy(ccp->colormap); + + /* free any palette that has been associated with this GdkColorContext */ + + init_palette(ccp); + + if (ccp->color_hash) { + g_hash_table_foreach(ccp->color_hash, + free_hash_entry, + NULL); + g_hash_table_destroy(ccp->color_hash); + } + + g_free(cc); +} + +gulong +gdk_color_context_get_pixel(GdkColorContext *cc, + gushort red, + gushort green, + gushort blue, + gint *failed) +{ + GdkColorContextPrivate *ccp; + + g_assert(cc != NULL); + g_assert(failed != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + *failed = FALSE; + + switch (ccp->mode) { + case GDK_CC_MODE_BW: { + gdouble value; + + red <<= 8; + green <<= 8; + blue <<= 8; + + value = red / 65535.0 * 0.30 + + green / 65535.0 * 0.59 + + blue / 65535.0 * 0.11; + + if (value > 0.5) + return ccp->white_pixel; + + return ccp->black_pixel; + } + + case GDK_CC_MODE_MY_GRAY: { + gulong ired, igreen, iblue; + + red <<= 8; + green <<= 8; + blue <<= 8; + + red = red * 0.30 + green * 0.59 + blue * 0.11; + green = 0; + blue = 0; + + if ((ired = red * (ccp->std_cmap.red_max + 1) / 0xffff) + > ccp->std_cmap.red_max) + ired = ccp->std_cmap.red_max; + + ired *= ccp->std_cmap.red_mult; + + if ((igreen = green * (ccp->std_cmap.green_max + 1) / 0xffff) + > ccp->std_cmap.green_max) + igreen = ccp->std_cmap.green_max; + + igreen *= ccp->std_cmap.green_mult; + + if ((iblue = blue * (ccp->std_cmap.blue_max + 1) / 0xffff) + > ccp->std_cmap.blue_max) + iblue = ccp->std_cmap.blue_max; + + iblue *= ccp->std_cmap.blue_mult; + + if (ccp->clut != NULL) + return ccp->clut[ccp->std_cmap.base_pixel + ired + igreen + iblue]; + + return ccp->std_cmap.base_pixel + ired + igreen + iblue; + } + + case GDK_CC_MODE_TRUE: { + gulong ired, igreen, iblue; + + red <<= 8; + green <<= 8; + blue <<= 8; + + if (ccp->clut == NULL) { + red >>= 16 - ccp->bits.red; + green >>= 16 - ccp->bits.green; + blue >>= 16 - ccp->bits.blue; + + ired = (red << ccp->shifts.red) & ccp->masks.red; + igreen = (green << ccp->shifts.green) & ccp->masks.green; + iblue = (blue << ccp->shifts.blue) & ccp->masks.blue; + + return ired | igreen | iblue; + } + + ired = ccp->clut[red * ccp->max_entry / 65535] & ccp->masks.red; + igreen = ccp->clut[green * ccp->max_entry / 65535] & ccp->masks.green; + iblue = ccp->clut[blue * ccp->max_entry / 65535] & ccp->masks.blue; + + return ired | igreen | iblue; + } + + case GDK_CC_MODE_PALETTE: + return gdk_color_context_get_pixel_from_palette(cc, &red, &green, &blue, failed); + + case GDK_CC_MODE_STD_CMAP: + default: { + GdkColor color; + GdkColor *result; + + red <<= 8; + green <<= 8; + blue <<= 8; + + color.red = red; + color.green = green; + color.blue = blue; + + result = g_hash_table_lookup(ccp->color_hash, &color); + + if (!result) { + color.red = red; + color.green = green; + color.blue = blue; + color.pixel = 0; + + if (!gdk_color_alloc(ccp->colormap, &color)) + *failed = TRUE; + else { + GdkColor *cnew; + + /* XXX: the following comment comes directly from + * XCC.c. I don't know if it is relevant for + * gdk_color_alloc() as it is for XAllocColor() + * - Federico + */ + /* + * I can't figure this out entirely, but it *is* possible + * that XAllocColor succeeds, even if the number of + * allocations we've made exceeds the number of available + * colors in the current colormap. And therefore it + * might be necessary for us to resize the CLUT. + */ + + if (ccp->num_allocated == ccp->max_colors) { + ccp->max_colors *= 2; + + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixel: " + "resizing CLUT to %i entries", + ccp->max_colors); + + ccp->clut = g_realloc(ccp->clut, + ccp->max_colors * sizeof(gulong)); + } + + /* Key and value are the same color structure */ + + cnew = g_new(GdkColor, 1); + *cnew = color; + g_hash_table_insert(ccp->color_hash, cnew, cnew); + + ccp->clut[ccp->num_allocated] = color.pixel; + ccp->num_allocated++; + return color.pixel; + } + } + + return result->pixel; + } + } +} + +void +gdk_color_context_get_pixels(GdkColorContext *cc, + gushort *reds, + gushort *greens, + gushort *blues, + gint ncolors, + gulong *colors, + gint *nallocated) +{ + GdkColorContextPrivate *ccp; + gint i, k, idx; + gint cmapsize, ncols = 0, nopen = 0, counter = 0; + gint bad_alloc = FALSE; + gint failed[MAX_IMAGE_COLORS], allocated[MAX_IMAGE_COLORS]; + GdkColor defs[MAX_IMAGE_COLORS], cmap[MAX_IMAGE_COLORS]; + gint exact_col = 0, subst_col = 0, close_col = 0, black_col = 0; + + g_assert(cc != NULL); + g_assert(reds != NULL); + g_assert(greens != NULL); + g_assert(blues != NULL); + g_assert(colors != NULL); + g_assert(nallocated != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + memset(defs, 0, MAX_IMAGE_COLORS * sizeof(GdkColor)); + memset(failed, 0, MAX_IMAGE_COLORS * sizeof(gint)); + memset(allocated, 0, MAX_IMAGE_COLORS * sizeof(gint)); + + /* Will only have a value if used by the progressive image loader */ + + ncols = *nallocated; + + *nallocated = 0; + + /* First allocate all pixels */ + + for (i = 0; i < ncolors; i++) { + /* colors[i] is only zero if the pixel at that location hasn't + * been allocated yet. This is a sanity check required for proper + * color allocation by the progressive image loader + */ + + if (colors[i] == 0) { + defs[i].red = reds[i]; + defs[i].green = greens[i]; + defs[i].blue = blues[i]; + + colors[i] = gdk_color_context_get_pixel(cc, reds[i], greens[i], blues[i], + &bad_alloc); + + /* successfully allocated, store it */ + + if (!bad_alloc) { + defs[i].pixel = colors[i]; + allocated[ncols++] = colors[i]; + } else + failed[nopen++] = i; + } + } + + *nallocated = ncols; + + /* all colors available, all done */ + + if ((ncols == ncolors) || (nopen == 0)) { + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixels: got all %i colors; " + "(%i colors allocated so far", ncolors, ccp->num_allocated); + + return; + } + + /* The fun part. We now try to allocate the colors we couldn't allocate + * directly. The first step will map a color onto its nearest color + * that has been allocated (either by us or someone else). If any colors + * remain unallocated, we map these onto the colors that we have allocated + * ourselves. + */ + + /* read up to MAX_IMAGE_COLORS colors of the current colormap */ + + cmapsize = MIN(ccp->num_colors, MAX_IMAGE_COLORS); + + /* see if the colormap has any colors to read */ + + if (cmapsize < 0) { + g_warning("gdk_color_context_get_pixels: oops! no colors available, " + "your images will look *really* ugly."); + + return; + } + +#ifdef DEBUG + exact_col = ncols; +#endif + + /* initialize pixels */ + + for (i = 0; i < cmapsize; i++) { + cmap[i].pixel = i; + cmap[i].red = cmap[i].green = cmap[i].blue = 0; + } + + /* read the colormap */ + + my_x_query_colors(ccp->colormap, cmap, cmapsize); + + /* speedup: downscale here instead of in the matching code */ + + for (i = 0; i < cmapsize; i++) { + cmap[i].red >>= 8; + cmap[i].green >>= 8; + cmap[i].blue >>= 8; + } + + /* get a close match for any unallocated colors */ + + counter = nopen; + nopen = 0; + idx = 0; + + do { + gint d, j, mdist, close, ri, gi, bi; + gint rd, gd, bd; + + i = failed[idx]; + + mdist = 1000000; + close = -1; + + /* Store these vals. Small performance increase as this skips three + * indexing operations in the loop code. + */ + + ri = reds[i]; + gi = greens[i]; + bi = blues[i]; + + /* Walk all colors in the colormap and see which one is the + * closest. Uses plain least squares. + */ + + for (j = 0; (j < cmapsize) && (mdist != 0); j++) { + rd = ri - cmap[j].red; + gd = gi - cmap[j].green; + bd = bi - cmap[j].blue; + + d = rd * rd + gd * gd + bd * bd; + + if (d < mdist) { + close = j; + mdist = d; + } + } + + if (close != -1) { + rd = cmap[close].red; + gd = cmap[close].green; + bd = cmap[close].blue; + + /* allocate */ + + colors[i] = gdk_color_context_get_pixel(cc, rd, gd, bd, &bad_alloc); + + /* store */ + + if (!bad_alloc) { + defs[i] = cmap[close]; + defs[i].pixel = colors[i]; + allocated[ncols++] = colors[i]; +#ifdef DEBUG + close_col++; +#endif + } else + failed[nopen++] = i; + } else + failed[nopen++] = i; + /* deal with in next stage if allocation failed */ + } while (++idx < counter); + + *nallocated = ncols; + + /* This is the maximum no. of allocated colors. See also the nopen == 0 + * note above. + */ + + if ((ncols == ncolors) || (nopen == 0)) { + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixels: got %i colors, %i exact and " + "%i close (%i colors allocated so far)", + ncolors, exact_col, close_col, ccp->num_allocated); + + return; + } + + /* Now map any remaining unallocated pixels into the colors we did get */ + + idx = 0; + + do { + gint d, mdist, close, ri, gi, bi; + gint j, rd, gd, bd; + + i = failed[idx]; + + mdist = 1000000; + close = -1; + + /* store */ + + ri = reds[i]; + gi = greens[i]; + bi = blues[i]; + + /* search allocated colors */ + + for (j = 0; (j < ncols) && (mdist != 0); j++) { + k = allocated[j]; + + rd = ri - defs[k].red; + gd = gi - defs[k].green; + bd = bi - defs[k].blue; + + d = rd * rd + gd * gd + bd * bd; + + if (d < mdist) { + close = k; + mdist = d; + } + } + + if (close < 0) { + /* too bad, map to black */ + + defs[i].pixel = ccp->black_pixel; + defs[i].red = defs[i].green = defs[i].blue = 0; +#ifdef DEBUG + black_col++; +#endif + } else { + defs[i] = defs[close]; +#ifdef DEBUG + subst_col++; +#endif + } + + colors[i] = defs[i].pixel; + } while (++idx < nopen); + + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixels: got %i colors, %i exact, %i close, " + "%i substituted, %i to black (%i colors allocated so far)", + ncolors, exact_col, close_col, subst_col, black_col, ccp->num_allocated); +} + +void +gdk_color_context_get_pixels_incremental(GdkColorContext *cc, + gushort *reds, + gushort *greens, + gushort *blues, + gint ncolors, + gint *used, + gulong *colors, + gint *nallocated) +{ + GdkColorContextPrivate *ccp; + gint i, k, idx; + gint cmapsize, ncols = 0, nopen = 0, counter = 0; + gint bad_alloc = FALSE; + gint failed[MAX_IMAGE_COLORS], allocated[MAX_IMAGE_COLORS]; + GdkColor defs[MAX_IMAGE_COLORS], cmap[MAX_IMAGE_COLORS]; + gint exact_col = 0, subst_col = 0, close_col = 0, black_col = 0; + + g_assert(cc != NULL); + g_assert(reds != NULL); + g_assert(greens != NULL); + g_assert(blues != NULL); + g_assert(used != NULL); + g_assert(colors != NULL); + g_assert(nallocated != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + memset(defs, 0, MAX_IMAGE_COLORS * sizeof(GdkColor)); + memset(failed, 0, MAX_IMAGE_COLORS * sizeof(gint)); + memset(allocated, 0, MAX_IMAGE_COLORS * sizeof(gint)); + + /* Will only have a value if used by the progressive image loader */ + + ncols = *nallocated; + + *nallocated = 0; + + /* First allocate all pixels */ + + for (i = 0; i < ncolors; i++) { + /* used[i] is only -1 if the pixel at that location hasn't + * been allocated yet. This is a sanity check required for proper + * color allocation by the progressive image loader. + * When colors[i] == 0 it indicates the slot is available for + * allocation. + */ + + if (used[i] != FALSE) { + if (colors[i] == 0) { + defs[i].red = reds[i]; + defs[i].green = greens[i]; + defs[i].blue = blues[i]; + + colors[i] = gdk_color_context_get_pixel(cc, reds[i], greens[i], blues[i], &bad_alloc); + + /* successfully allocated, store it */ + + if (!bad_alloc) { + defs[i].pixel = colors[i]; + allocated[ncols++] = colors[i]; + } else + failed[nopen++] = i; + } +#ifdef DEBUG + else + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixels_incremental: " + "pixel at slot %i already allocated, skipping", i); +#endif + } + } + + *nallocated = ncols; + + if ((ncols == ncolors) || (nopen == 0)) { + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixels_incremental: got all %i colors " + "(%i colors allocated so far)", + ncolors, ccp->num_allocated); + + return; + } + + cmapsize = MIN(ccp->num_colors, MAX_IMAGE_COLORS); + + if (cmapsize < 0) { + g_warning("gdk_color_context_get_pixels_incremental: oops! " + "No colors available images will look *really* ugly."); + return; + } + +#ifdef DEBUG + exact_col = ncols; +#endif + + /* initialize pixels */ + + for (i = 0; i < cmapsize; i++) { + cmap[i].pixel = i; + cmap[i].red = cmap[i].green = cmap[i].blue = 0; + } + + /* read and downscale */ + + my_x_query_colors(ccp->colormap, cmap, cmapsize); + + for (i = 0; i < cmapsize; i++) { + cmap[i].red >>= 8; + cmap[i].green >>= 8; + cmap[i].blue >>= 8; + } + + /* now match any unallocated colors */ + + counter = nopen; + nopen = 0; + idx = 0; + + do { + gint d, j, mdist, close, ri, gi, bi; + gint rd, gd, bd; + + i = failed[idx]; + + mdist = 1000000; + close = -1; + + /* store */ + + ri = reds[i]; + gi = greens[i]; + bi = blues[i]; + + for (j = 0; (j < cmapsize) && (mdist != 0); j++) { + rd = ri - cmap[j].red; + gd = gi - cmap[j].green; + bd = bi - cmap[j].blue; + + d = rd * rd + gd * gd + bd * bd; + + if (d < mdist) { + close = j; + mdist = d; + } + } + + if (close != -1) { + rd = cmap[close].red; + gd = cmap[close].green; + bd = cmap[close].blue; + + /* allocate */ + + colors[i] = gdk_color_context_get_pixel(cc, rd, gd, bd, &bad_alloc); + + /* store */ + + if (!bad_alloc) { + defs[i] = cmap[close]; + defs[i].pixel = colors[i]; + allocated[ncols++] = colors[i]; +#ifdef DEBUG + close_col++; +#endif + } else + failed[nopen++] = i; + } else + failed[nopen++] = i; + /* deal with in next stage if allocation failed */ + } while (++idx < counter); + + *nallocated = ncols; + + if ((ncols == ncolors) || (nopen == 0)) { + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixels_incremental: " + "got %i colors, %i exact and %i close " + "(%i colors allocated so far)", + ncolors, exact_col, close_col, ccp->num_allocated); + + return; + } + + /* map remaining unallocated pixels into colors we did get */ + + idx = 0; + + do { + gint d, mdist, close, ri, gi, bi; + gint j, rd, gd, bd; + + i = failed[idx]; + + mdist = 1000000; + close = -1; + + ri = reds[i]; + gi = greens[i]; + bi = blues[i]; + + /* search allocated colors */ + + for (j = 0; (j < ncols) && (mdist != 0); j++) { + k = allocated[j]; + + /* downscale */ + + rd = ri - defs[k].red; + gd = gi - defs[k].green; + bd = bi - defs[k].blue; + + d = rd * rd + gd * gd + bd * bd; + + if (d < mdist) { + close = k; + mdist = d; + } + } + + if (close < 0) { + /* too bad, map to black */ + + defs[i].pixel = ccp->black_pixel; + defs[i].red = defs[i].green = defs[i].blue = 0; +#ifdef DEBUG + black_col++; +#endif + } else { + defs[i] = defs[close]; +#ifdef DEBUG + subst_col++; +#endif + } + + colors[i] = defs[i].pixel; + } while (++idx < nopen); + + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixels_incremental: " + "got %i colors, %i exact, %i close, %i substituted, %i to black " + "(%i colors allocated so far)", + ncolors, exact_col, close_col, subst_col, black_col, ccp->num_allocated); +} + +gint +gdk_color_context_get_num_colors(GdkColorContext *cc) +{ + g_assert(cc != NULL); + + return ((GdkColorContextPrivate *) cc)->num_colors; +} + +gint +gdk_color_context_query_color(GdkColorContext *cc, + GdkColor *color) +{ + return gdk_color_context_query_colors(cc, color, 1); +} + +gint +gdk_color_context_query_colors(GdkColorContext *cc, + GdkColor *colors, + gint num_colors) +{ + GdkColorContextPrivate *ccp; + gint i; + GdkColor *tc; + + g_assert(cc != NULL); + g_assert(colors != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + switch (ccp->mode) { + case GDK_CC_MODE_BW: + for (i = 0, tc = colors; i < num_colors; i++, tc++) { + if (tc->pixel == ccp->white_pixel) + tc->red = tc->green = tc->blue = 65535; + else + tc->red = tc->green = tc->blue = 0; + } + break; + + case GDK_CC_MODE_TRUE: + if (ccp->clut == NULL) + for (i = 0, tc = colors; i < num_colors; i++, tc++) { + tc->red = (tc->pixel & ccp->masks.red) * 65535 / ccp->masks.red; + tc->green = (tc->pixel & ccp->masks.green) * 65535 / ccp->masks.green; + tc->blue = (tc->pixel & ccp->masks.blue) * 65535 / ccp->masks.blue; + } + else { + my_x_query_colors(ccp->colormap, colors, num_colors); + return 1; + } + break; + + case GDK_CC_MODE_STD_CMAP: + default: + if (ccp->cmap == NULL) { + my_x_query_colors(ccp->colormap, colors, num_colors); + return 1; + } else { + gint first, last, half; + gulong half_pixel; + + for (i = 0, tc = colors; i < num_colors; i++) { + first = 0; + last = ccp->num_colors - 1; + + while (first <= last) { + half = (first + last) / 2; + half_pixel = ccp->cmap[half].pixel; + + if (tc->pixel == half_pixel) { + tc->red = ccp->cmap[half].red; + tc->green = ccp->cmap[half].green; + tc->blue = ccp->cmap[half].blue; + first = last + 1; /* false break */ + } else { + if (tc->pixel > half_pixel) + first = half + 1; + else + last = half - 1; + } + } + } + return 1; + } + break; + } + return 1; +} + +gint +gdk_color_context_add_palette(GdkColorContext *cc, + GdkColor *palette, + gint num_palette) +{ + GdkColorContextPrivate *ccp; + gint i, j, erg; + gushort r, g, b; + gulong pixel[1]; + + g_assert(cc != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + /* initialize this palette (will also erase previous palette as well) */ + + init_palette(ccp); + + /* restore previous mode if we aren't adding a new palette */ + + if (num_palette == 0) { + /* GDK_CC_MODE_STD_CMAP uses a hash table, so we'd better initialize one */ + + /* XXX: here, the hash table is already initialized */ + + return 0; + } + + /* Initialize a hash table for this palette (we need one for allocating + * the pixels in the palette using the current settings) + */ + + if (ccp->color_hash == NULL) + ccp->color_hash = g_hash_table_new(hash_color, compare_colors); + + /* copy incoming palette */ + + ccp->palette = g_new0(GdkColor, num_palette); + + j = 0; + + for (i = 0; i < num_palette; i++) { + erg = 0; + pixel[0] = 0; + + /* try to allocate this color */ + + r = palette[i].red; + g = palette[i].green; + b = palette[i].blue; + + gdk_color_context_get_pixels(cc, &r, &g, &b, 1, pixel, &erg); + + /* only store if we succeed */ + + if (erg) { + /* store in palette */ + + ccp->palette[j].red = r; + ccp->palette[j].green = g; + ccp->palette[j].blue = b; + ccp->palette[j].pixel = pixel[0]; + + /* move to next slot */ + + j++; + } + } + + /* resize to fit */ + + if (j != num_palette) + ccp->palette = g_realloc(ccp->palette, j * sizeof(GdkColor)); + + /* clear the hash table, we don't use it when dithering */ + + if (ccp->color_hash) { + g_hash_table_destroy(ccp->color_hash); + ccp->color_hash = NULL; + } + + /* store real palette size */ + + ccp->num_palette = j; + + /* switch to palette mode */ + + ccp->mode = GDK_CC_MODE_PALETTE; + + /* sort palette */ + + qsort(ccp->palette, ccp->num_palette, sizeof(GdkColor), pixel_sort); + + ccp->fast_dither = NULL; + + return j; +} + +void +gdk_color_context_init_dither(GdkColorContext *cc) +{ + GdkColorContextPrivate *ccp; + gint rr, gg, bb, err, erg, erb; + gint success = FALSE; + + g_assert(cc != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + /* now we can initialize the fast dither matrix */ + + if(ccp->fast_dither == NULL) + ccp->fast_dither = g_new(GdkColorContextDither, 1); + + /* Fill it. We ignore unsuccessful allocations, they are just mapped + * to black instead */ + + for (rr = 0; rr < 32; rr++) + for (gg = 0; gg < 32; gg++) + for (bb = 0; bb < 32; bb++) { + err = (rr << 3) | (rr >> 2); + erg = (gg << 3) | (gg >> 2); + erb = (bb << 3) | (bb >> 2); + + ccp->fast_dither->fast_rgb[rr][gg][bb] = + gdk_color_context_get_index_from_palette(cc, &err, &erg, &erb, &success); + ccp->fast_dither->fast_err[rr][gg][bb] = err; + ccp->fast_dither->fast_erg[rr][gg][bb] = erg; + ccp->fast_dither->fast_erb[rr][gg][bb] = erb; + } +} + +void +gdk_color_context_free_dither(GdkColorContext *cc) +{ + GdkColorContextPrivate *ccp; + + g_assert(cc != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + if (ccp->fast_dither) + g_free(ccp->fast_dither); + + ccp->fast_dither = NULL; +} + +gulong +gdk_color_context_get_pixel_from_palette(GdkColorContext *cc, + gushort *red, + gushort *green, + gushort *blue, + gint *failed) +{ + GdkColorContextPrivate *ccp; + gulong pixel = 0; + gint dif, dr, dg, db, j = -1; + gint mindif = 0x7fffffff; + gint err = 0, erg = 0, erb = 0; + gint i; + + g_assert(cc != NULL); + g_assert(red != NULL); + g_assert(green != NULL); + g_assert(blue != NULL); + g_assert(failed != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + *failed = FALSE; + + for (i = 0; i < ccp->num_palette; i++) { + dr = *red - ccp->palette[i].red; + dg = *green - ccp->palette[i].green; + db = *blue - ccp->palette[i].blue; + + dif = dr * dr + dg * dg + db * db; + + if (dif < mindif) { + mindif = dif; + j = i; + pixel = ccp->palette[i].pixel; + err = dr; + erg = dg; + erb = db; + + if (mindif == 0) + break; + } + } + + /* we failed to map onto a color */ + + if (j == -1) + *failed = TRUE; + else { + *red = ABS(err); + *green = ABS(erg); + *blue = ABS(erb); + } + + return pixel; +} + +guchar +gdk_color_context_get_index_from_palette(GdkColorContext *cc, + gint *red, + gint *green, + gint *blue, + gint *failed) +{ + GdkColorContextPrivate *ccp; + gint dif, dr, dg, db, j = -1; + gint mindif = 0x7fffffff; + gint err = 0, erg = 0, erb = 0; + gint i; + + g_assert(cc != NULL); + g_assert(red != NULL); + g_assert(green != NULL); + g_assert(blue != NULL); + g_assert(failed != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + *failed = FALSE; + + for (i = 0; i < ccp->num_palette; i++) { + dr = *red - ccp->palette[i].red; + dg = *green - ccp->palette[i].green; + db = *blue - ccp->palette[i].blue; + + dif = dr * dr + dg * dg + db * db; + + if (dif < mindif) { + mindif = dif; + j = i; + err = dr; + erg = dg; + erb = db; + + if (mindif == 0) + break; + } + } + + /* we failed to map onto a color */ + + if (j == -1) { + *failed = TRUE; + j = 0; + } else { + /* return error fractions */ + + *red = err; + *green = erg; + *blue = erb; + } + + return j; +} diff --git a/gdk/gdkprivate.h b/gdk/gdkprivate.h index 9c0e9918d4..e15ef6a239 100644 --- a/gdk/gdkprivate.h +++ b/gdk/gdkprivate.h @@ -35,14 +35,15 @@ extern "C" { #endif /* __cplusplus */ -typedef struct _GdkWindowPrivate GdkWindowPrivate; -typedef struct _GdkWindowPrivate GdkPixmapPrivate; -typedef struct _GdkImagePrivate GdkImagePrivate; -typedef struct _GdkGCPrivate GdkGCPrivate; -typedef struct _GdkColormapPrivate GdkColormapPrivate; -typedef struct _GdkVisualPrivate GdkVisualPrivate; -typedef struct _GdkFontPrivate GdkFontPrivate; -typedef struct _GdkCursorPrivate GdkCursorPrivate; +typedef struct _GdkWindowPrivate GdkWindowPrivate; +typedef struct _GdkWindowPrivate GdkPixmapPrivate; +typedef struct _GdkImagePrivate GdkImagePrivate; +typedef struct _GdkGCPrivate GdkGCPrivate; +typedef struct _GdkColormapPrivate GdkColormapPrivate; +typedef struct _GdkVisualPrivate GdkVisualPrivate; +typedef struct _GdkFontPrivate GdkFontPrivate; +typedef struct _GdkCursorPrivate GdkCursorPrivate; +typedef struct _GdkColorContextPrivate GdkColorContextPrivate; struct _GdkWindowPrivate @@ -151,7 +152,7 @@ typedef struct _GdkDndGlobals GdkDndGlobals; #ifdef USE_XIM -struct _GdkICPrivate +struct _GdkICPrivate { XIC xic; GdkIMStyle style; @@ -161,6 +162,59 @@ typedef struct _GdkICPrivate GdkICPrivate; #endif /* USE_XIM */ +struct _GdkColorContextPrivate +{ + GdkColorContext color_context; + Display *xdisplay; + GdkVisual *visual; + GdkColormap *colormap; + + gint num_colors; /* available no. of colors in colormap */ + gint max_colors; /* maximum no. of colors */ + gint num_allocated; /* no. of allocated colors */ + + GdkColorContextMode mode; + gint need_to_free_colormap; + GdkAtom std_cmap_atom; + + XStandardColormap std_cmap; + gulong *clut; /* color look-up table */ + GdkColor *cmap; /* colormap */ + + GHashTable *color_hash; /* hash table of allocated colors */ + GdkColor *palette; /* preallocated palette */ + gint num_palette; /* size of palette */ + + GdkColorContextDither *fast_dither; /* fast dither matrix */ + + struct + { + gint red; + gint green; + gint blue; + } shifts; + + struct + { + gulong red; + gulong green; + gulong blue; + } masks; + + + struct { + gint red; + gint green; + gint blue; + } bits; + + gulong max_entry; + + gulong black_pixel; + gulong white_pixel; +}; + + void gdk_window_init (void); void gdk_visual_init (void); diff --git a/gdk/gdktypes.h b/gdk/gdktypes.h index 8f8022f773..be2a60dedd 100644 --- a/gdk/gdktypes.h +++ b/gdk/gdktypes.h @@ -40,23 +40,25 @@ extern "C" { /* Type definitions for the basic structures. */ -typedef gulong GdkAtom; -typedef struct _GdkColor GdkColor; -typedef struct _GdkColormap GdkColormap; -typedef struct _GdkVisual GdkVisual; -typedef struct _GdkWindowAttr GdkWindowAttr; -typedef struct _GdkWindow GdkWindow; -typedef struct _GdkWindow GdkPixmap; -typedef struct _GdkWindow GdkBitmap; -typedef struct _GdkWindow GdkDrawable; -typedef struct _GdkImage GdkImage; -typedef struct _GdkGCValues GdkGCValues; -typedef struct _GdkGC GdkGC; -typedef struct _GdkPoint GdkPoint; -typedef struct _GdkRectangle GdkRectangle; -typedef struct _GdkSegment GdkSegment; -typedef struct _GdkFont GdkFont; -typedef struct _GdkCursor GdkCursor; +typedef gulong GdkAtom; +typedef struct _GdkColor GdkColor; +typedef struct _GdkColormap GdkColormap; +typedef struct _GdkVisual GdkVisual; +typedef struct _GdkWindowAttr GdkWindowAttr; +typedef struct _GdkWindow GdkWindow; +typedef struct _GdkWindow GdkPixmap; +typedef struct _GdkWindow GdkBitmap; +typedef struct _GdkWindow GdkDrawable; +typedef struct _GdkImage GdkImage; +typedef struct _GdkGCValues GdkGCValues; +typedef struct _GdkGC GdkGC; +typedef struct _GdkPoint GdkPoint; +typedef struct _GdkRectangle GdkRectangle; +typedef struct _GdkSegment GdkSegment; +typedef struct _GdkFont GdkFont; +typedef struct _GdkCursor GdkCursor; +typedef struct _GdkColorContextDither GdkColorContextDither; +typedef struct _GdkColorContext GdkColorContext; typedef struct _GdkEventAny GdkEventAny; typedef struct _GdkEventExpose GdkEventExpose; @@ -566,6 +568,28 @@ typedef void (*GdkInputFunction) (gpointer data, gint source, GdkInputCondition condition); + +/* Color Context modes. + * + * GDK_CC_MODE_UNDEFINED - unknown + * GDK_CC_MODE_BW - default B/W + * GDK_CC_MODE_STD_CMAP - has a standard colormap + * GDK_CC_MODE_TRUE - is a TrueColor/DirectColor visual + * GDK_CC_MODE_MY_GRAY - my grayramp + * GDK_CC_MODE_PALETTE - has a pre-allocated palette + */ + +typedef enum +{ + GDK_CC_MODE_UNDEFINED, + GDK_CC_MODE_BW, + GDK_CC_MODE_STD_CMAP, + GDK_CC_MODE_TRUE, + GDK_CC_MODE_MY_GRAY, + GDK_CC_MODE_PALETTE +} GdkColorContextMode; + + /* The color type. * A color consists of red, green and blue values in the * range 0-65535 and a pixel value. The pixel value is highly @@ -718,6 +742,19 @@ struct _GdkCursor GdkCursorType type; }; +struct _GdkColorContextDither +{ + gint fast_rgb[32][32][32]; /* quick look-up table for faster rendering */ + gint fast_err[32][32][32]; /* internal RGB error information */ + gint fast_erg[32][32][32]; + gint fast_erb[32][32][32]; +}; + +struct _GdkColorContext +{ + gint dummy; +}; + /* Types for XInput support */ struct _GdkDeviceInfo diff --git a/gdk/x11/gdkcc-x11.c b/gdk/x11/gdkcc-x11.c new file mode 100644 index 0000000000..0b089208d2 --- /dev/null +++ b/gdk/x11/gdkcc-x11.c @@ -0,0 +1,1679 @@ +/* GDK - The GIMP Drawing Kit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Color Context module + * Copyright 1994,1995 John L. Cwikla + * Copyright (C) 1997 by Ripley Software Development + * Copyright (C) 1997 by Federico Mena (port to Gtk/Gdk) + */ + +/* Copyright 1994,1995 John L. Cwikla + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appears in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation, and that the name of John L. Cwikla or + * Wolfram Research, Inc not be used in advertising or publicity + * pertaining to distribution of the software without specific, written + * prior permission. John L. Cwikla and Wolfram Research, Inc make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * John L. Cwikla and Wolfram Research, Inc disclaim all warranties with + * regard to this software, including all implied warranties of + * merchantability and fitness, in no event shall John L. Cwikla or + * Wolfram Research, Inc be liable for any special, indirect or + * consequential damages or any damages whatsoever resulting from loss of + * use, data or profits, whether in an action of contract, negligence or + * other tortious action, arising out of or in connection with the use or + * performance of this software. + * + * Author: + * John L. Cwikla + * X Programmer + * Wolfram Research Inc. + * + * cwikla@wri.com + */ + +/* NOTES: + * + * - When a CC is destroyed, remember to destroy the hash table properly. + */ + + +#include <X11/Xlib.h> +#include <stdlib.h> +#include <string.h> +#include "gdk.h" +#include "gdkprivate.h" +#include "gdkx.h" + + +#define MAX_IMAGE_COLORS 256 + + +static guint +hash_color(gpointer key) +{ + GdkColor *color = key; + + return (color->red * 33023 + color->green * 30013 + color->blue * 27011); +} + +static gint +compare_colors(gpointer a, gpointer b) +{ + GdkColor *aa = a; + GdkColor *bb = b; + + return ((aa->red == bb->red) && (aa->green == bb->green) && (aa->blue == bb->blue)); +} + +static void +free_hash_entry(gpointer key, gpointer value, gpointer user_data) +{ + g_free(key); /* key and value are the same GdkColor */ +} + +static int +pixel_sort(const void *a, const void *b) +{ + return ((GdkColor *) a)->pixel - ((GdkColor *) b)->pixel; +} + +/* XXX: This function does an XQueryColors() the hard way, because there is + * no corresponding function in Gdk. + */ + +static void +my_x_query_colors(GdkColormap *colormap, + GdkColor *colors, + gint ncolors) +{ + XColor *xcolors; + gint i; + + xcolors = g_new(XColor, ncolors); + for (i = 0; i < ncolors; i++) + xcolors[i].pixel = colors[i].pixel; + + XQueryColors(gdk_display, GDK_COLORMAP_XCOLORMAP(colormap), xcolors, ncolors); + + for (i = 0; i < ncolors; i++) { + colors[i].red = xcolors[i].red; + colors[i].green = xcolors[i].green; + colors[i].blue = xcolors[i].blue; + } + + g_free(xcolors); +} + +static void +query_colors(GdkColorContextPrivate *cc) +{ + gint i; + + cc->cmap = g_new(GdkColor, cc->num_colors); + + for (i = 0; i < cc->num_colors; i++) + cc->cmap[i].pixel = cc->clut ? cc->clut[i] : cc->std_cmap.base_pixel + i; + + my_x_query_colors(cc->colormap, cc->cmap, cc->num_colors); + + qsort(cc->cmap, cc->num_colors, sizeof(GdkColor), pixel_sort); +} + +static void +init_bw(GdkColorContextPrivate *cc) +{ + GdkColor color; + + g_warning("init_bw: failed to allocate colors, falling back to black and white"); + + cc->mode = GDK_CC_MODE_BW; + + color.red = color.green = color.blue = 0; + if (!gdk_color_alloc(cc->colormap, &color)) + cc->black_pixel = 0; + else + cc->black_pixel = color.pixel; + + color.red = color.green = color.blue = 0xffff; + if (!gdk_color_alloc(cc->colormap, &color)) + cc->white_pixel = cc->black_pixel ? 0 : 1; + else + cc->white_pixel = color.pixel; + + cc->num_colors = 2; +} + +static void +init_gray(GdkColorContextPrivate *cc) +{ + GdkColor *clrs, *cstart; + gint i; + gdouble dinc; + + cc->num_colors = GDK_VISUAL_XVISUAL(cc->visual)->map_entries; + + cc->clut = g_new(gulong, cc->num_colors); + cstart = g_new(GdkColor, cc->num_colors); + +retrygray: + + dinc = 65535.0 / (cc->num_colors - 1); + + clrs = cstart; + + for (i = 0; i < cc->num_colors; i++) { + clrs->red = clrs->green = clrs->blue = dinc * i; + + if (!gdk_color_alloc(cc->colormap, clrs)) { + gdk_colors_free(cc->colormap, cc->clut, i, 0); + + cc->num_colors /= 2; + + if (cc->num_colors > 1) + goto retrygray; + else { + g_free(cc->clut); + cc->clut = NULL; + init_bw(cc); + g_free(cstart); + return; + } + } + + cc->clut[i] = clrs++->pixel; + } + + g_free(cstart); + + /* XXX: is this the right thing to do? */ + cc->std_cmap.colormap = GDK_COLORMAP_XCOLORMAP(cc->colormap); + cc->std_cmap.base_pixel = 0; + cc->std_cmap.red_max = cc->num_colors - 1; + cc->std_cmap.green_max = 0; + cc->std_cmap.blue_max = 0; + cc->std_cmap.red_mult = 1; + cc->std_cmap.green_mult = 0; + cc->std_cmap.blue_mult = 0; + + cc->white_pixel = WhitePixel(cc->xdisplay, gdk_screen); + cc->black_pixel = BlackPixel(cc->xdisplay, gdk_screen); + + query_colors(cc); + + cc->mode = GDK_CC_MODE_MY_GRAY; +} + +static void +init_color(GdkColorContextPrivate *cc) +{ + gint cubeval; + + cubeval = 1; + while ((cubeval * cubeval * cubeval) < GDK_VISUAL_XVISUAL(cc->visual)->map_entries) + cubeval++; + cubeval--; + + cc->num_colors = cubeval * cubeval * cubeval; + + cc->std_cmap.red_max = cubeval - 1; + cc->std_cmap.green_max = cubeval - 1; + cc->std_cmap.blue_max = cubeval - 1; + cc->std_cmap.red_mult = cubeval * cubeval; + cc->std_cmap.green_mult = cubeval; + cc->std_cmap.blue_mult = 1; + cc->std_cmap.base_pixel = 0; + + cc->white_pixel = WhitePixel(cc->xdisplay, gdk_screen); + cc->black_pixel = BlackPixel(cc->xdisplay, gdk_screen); + cc->num_colors = DisplayCells(cc->xdisplay, gdk_screen); + + /* a CLUT for storing allocated pixel indices */ + + cc->max_colors = cc->num_colors; + cc->clut = g_new(gulong, cc->max_colors); + + for (cubeval = 0; cubeval < cc->max_colors; cubeval++) + cc->clut[cubeval] = cubeval; + + query_colors(cc); + + cc->mode = GDK_CC_MODE_STD_CMAP; +} + + +static void +init_true_color(GdkColorContextPrivate *cc) +{ + gulong rmask, gmask, bmask; + + cc->mode = GDK_CC_MODE_TRUE; + + /* Red */ + + rmask = cc->masks.red = cc->visual->red_mask; + + cc->shifts.red = 0; + cc->bits.red = 0; + + while (!(rmask & 1)) { + rmask >>= 1; + cc->shifts.red++; + } + + while (rmask & 1) { + rmask >>= 1; + cc->bits.red++; + } + + /* Green */ + + gmask = cc->masks.green = cc->visual->green_mask; + + cc->shifts.green = 0; + cc->bits.green = 0; + + while (!(gmask & 1)) { + gmask >>= 1; + cc->shifts.green++; + } + + while (gmask & 1) { + gmask >>= 1; + cc->bits.green++; + } + + /* Blue */ + + bmask = cc->masks.blue = cc->visual->blue_mask; + + cc->shifts.blue = 0; + cc->bits.blue = 0; + + while (!(bmask & 1)) { + bmask >>= 1; + cc->shifts.blue++; + } + + while (bmask & 1) { + bmask >>= 1; + cc->bits.blue++; + } + + cc->num_colors = (cc->visual->red_mask | cc->visual->green_mask | cc->visual->blue_mask) + 1; + cc->white_pixel = WhitePixel(cc->xdisplay, gdk_screen); + cc->black_pixel = BlackPixel(cc->xdisplay, gdk_screen); +} + +static void +init_direct_color(GdkColorContextPrivate *cc) +{ + gint n, count; + GdkColor *clrs, *cstart; + gulong rval, gval, bval; + gulong *rtable; + gulong *gtable; + gulong *btable; + gdouble dinc; + + init_true_color(cc); /* for shift stuff */ + + rval = cc->visual->red_mask >> cc->shifts.red; + gval = cc->visual->green_mask >> cc->shifts.green; + bval = cc->visual->blue_mask >> cc->shifts.blue; + + rtable = g_new(gulong, rval + 1); + gtable = g_new(gulong, gval + 1); + btable = g_new(gulong, bval + 1); + + cc->max_entry = MAX(rval, gval); + cc->max_entry = MAX(cc->max_entry, bval); + + cstart = g_new(GdkColor, cc->max_entry + 1); + cc->clut = g_new(gulong, cc->max_entry + 1); + +retrydirect: + + for (n = 0; n < rval; n++) + rtable[n] = rval ? (65535.0 / rval * n) : 0; + + for (n = 0; n < gval; n++) + gtable[n] = gval ? (65535.0 / gval * n) : 0; + + for (n = 0; n < bval; n++) + btable[n] = bval ? (65535.0 / bval * n) : 0; + + cc->max_entry = MAX(rval, gval); + cc->max_entry = MAX(cc->max_entry, bval); + + count = 0; + clrs = cstart; + cc->num_colors = (rval + 1) * (gval + 1) * (bval + 1); + + for (n = 0; n < cc->max_entry; n++) { + dinc = (double) n / cc->max_entry; + + clrs->red = rtable[(int) (dinc * rval)]; + clrs->green = gtable[(int) (dinc * gval)]; + clrs->blue = btable[(int) (dinc * bval)]; + + if (gdk_color_alloc(cc->colormap, clrs)) { + cc->clut[count++] = clrs->pixel; + clrs++; + } else { + gdk_colors_free(cc->colormap, cc->clut, count, 0); + + rval >>= 1; + gval >>= 1; + bval >>= 1; + + cc->masks.red = (cc->masks.red >> 1) & cc->visual->red_mask; + cc->masks.green = (cc->masks.green >> 1) & cc->visual->green_mask; + cc->masks.blue = (cc->masks.blue >> 1) & cc->visual->blue_mask; + + cc->shifts.red++; + cc->shifts.green++; + cc->shifts.blue++; + + cc->bits.red--; + cc->bits.green--; + cc->bits.blue--; + + cc->num_colors = (rval + 1) * (gval + 1) * (bval + 1); + + if (cc->num_colors >1) + goto retrydirect; + else { + g_free(cc->clut); + cc->clut = NULL; + init_bw(cc); + break; + } + } + } + + /* Update allocated color count; original num_colors is max_entry, which + * is not necessarily the same as the really allocated number of colors. + */ + + cc->num_colors = count; + + g_free(rtable); + g_free(gtable); + g_free(btable); + g_free(cstart); +} + +static void +init_palette(GdkColorContextPrivate *cc) +{ + /* restore correct mode for this cc */ + + switch (cc->visual->type) { + case GDK_VISUAL_STATIC_GRAY: + case GDK_VISUAL_GRAYSCALE: + if (GDK_VISUAL_XVISUAL(cc->visual)->map_entries == 2) + cc->mode = GDK_CC_MODE_BW; + else + cc->mode = GDK_CC_MODE_MY_GRAY; + break; + + case GDK_VISUAL_TRUE_COLOR: + case GDK_VISUAL_DIRECT_COLOR: + cc->mode = GDK_CC_MODE_TRUE; + break; + + case GDK_VISUAL_STATIC_COLOR: + case GDK_VISUAL_PSEUDO_COLOR: + cc->mode = GDK_CC_MODE_STD_CMAP; + break; + + default: + cc->mode = GDK_CC_MODE_UNDEFINED; + break; + } + + /* previous palette */ + + if (cc->num_palette) + g_free(cc->palette); + + if (cc->fast_dither) + g_free(cc->fast_dither); + + /* clear hash table if present */ + + if (cc->color_hash) { + /* XXX: quick-and-dirty way to remove everything */ + + g_hash_table_destroy(cc->color_hash); + cc->color_hash = g_hash_table_new(hash_color, compare_colors); + } + + cc->palette = NULL; + cc->num_palette = 0; + cc->fast_dither = NULL; +} + +GdkColorContext * +gdk_color_context_new(GdkVisual *visual, + GdkColormap *colormap) +{ + gint use_private_colormap = FALSE; /* XXX: maybe restore full functionality later? */ + GdkColorContextPrivate *cc; + gint retry_count; + GdkColormap *default_colormap; + + g_assert(visual != NULL); + g_assert(colormap != NULL); + + cc = g_new(GdkColorContextPrivate, 1); + + cc->xdisplay = gdk_display; + cc->visual = visual; + cc->colormap = colormap; + cc->clut = NULL; + cc->cmap = NULL; + cc->mode = GDK_CC_MODE_UNDEFINED; + cc->need_to_free_colormap = FALSE; + + cc->color_hash = NULL; + cc->palette = NULL; + cc->num_palette = 0; + cc->fast_dither = NULL; + + default_colormap = gdk_colormap_get_system(); + + retry_count = 0; + + while (retry_count < 2) { + /* Only create a private colormap if the visual found isn't equal + * to the default visual and we don't have a private colormap, + * -or- if we are instructed to create a private colormap (which + * never is the case for XmHTML). + */ + + if (use_private_colormap + || ((cc->visual != gdk_visual_get_system()) /* default visual? */ + && (GDK_COLORMAP_XCOLORMAP(colormap) == GDK_COLORMAP_XCOLORMAP(default_colormap)))) { + g_warning("gdk_color_context_new: non-default visual detected, " + "using private colormap"); + + cc->colormap = gdk_colormap_new(cc->visual, FALSE); + + cc->need_to_free_colormap = (GDK_COLORMAP_XCOLORMAP(colormap) + != GDK_COLORMAP_XCOLORMAP(default_colormap)); + } + + switch (visual->type) { + case GDK_VISUAL_STATIC_GRAY: + case GDK_VISUAL_GRAYSCALE: + if (gdk_debug_level >= 1) + g_print("gdk_color_context_new: visual class is %s", + (visual->type == GDK_VISUAL_STATIC_GRAY) ? + "GDK_VISUAL_STATIC_GRAY" : + "GDK_VISUAL_GRAYSCALE"); + + if (GDK_VISUAL_XVISUAL(cc->visual)->map_entries == 2) + init_bw(cc); + else + init_gray(cc); + + break; + + case GDK_VISUAL_TRUE_COLOR: /* shifts */ + if (gdk_debug_level >= 1) + g_print("gdk_color_context_new: visual class is " + "GDK_VISUAL_TRUE_COLOR"); + + init_true_color(cc); + break; + + case GDK_VISUAL_DIRECT_COLOR: /* shifts and fake CLUT */ + if (gdk_debug_level >= 1) + g_print("gdk_color_context_new: visual class is " + "GDK_VISUAL_DIRECT_COLOR"); + + init_direct_color(cc); + break; + + case GDK_VISUAL_STATIC_COLOR: + case GDK_VISUAL_PSEUDO_COLOR: + if (gdk_debug_level >= 1) + g_print("gdk_color_context_new: visual class is %s", + (visual->type == GDK_VISUAL_STATIC_COLOR) ? + "GDK_VISUAL_STATIC_COLOR" : + "GDK_VISUAL_PSEUDO_COLOR"); + + init_color(cc); + break; + + default: + g_assert_not_reached(); + } + + if ((cc->mode == GDK_CC_MODE_BW) && (cc->visual->depth > 1)) { + use_private_colormap = TRUE; + retry_count++; + } else + break; + } + + /* no. of colors allocated yet */ + + cc->num_allocated = 0; + + if (gdk_debug_level >= 1) + g_print("gdk_color_context_new: screen depth is %i, no. of colors is %i", + cc->visual->depth, cc->num_colors); + + /* check if we need to initialize a hash table */ + + if ((cc->mode == GDK_CC_MODE_STD_CMAP) || (cc->mode == GDK_CC_MODE_UNDEFINED)) + cc->color_hash = g_hash_table_new(hash_color, compare_colors); + + return (GdkColorContext *) cc; +} + +GdkColorContext * +gdk_color_context_new_mono(GdkVisual *visual, + GdkColormap *colormap) +{ + GdkColorContextPrivate *cc; + + g_assert(visual != NULL); + g_assert(colormap != NULL); + + cc = g_new(GdkColorContextPrivate, 1); + + cc->xdisplay = gdk_display; + cc->visual = visual; + cc->colormap = colormap; + cc->clut = NULL; + cc->cmap = NULL; + cc->mode = GDK_CC_MODE_UNDEFINED; + cc->need_to_free_colormap = FALSE; + + init_bw(cc); + + return (GdkColorContext *) cc; +} + +/* This doesn't currently free black/white, hmm... */ + +void +gdk_color_context_free(GdkColorContext *cc) +{ + GdkColorContextPrivate *ccp; + + g_assert(cc != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + if ((ccp->visual->type == GDK_VISUAL_STATIC_COLOR) + || (ccp->visual->type == GDK_VISUAL_PSEUDO_COLOR)) { + gdk_colors_free(ccp->colormap, ccp->clut, ccp->num_allocated, 0); + g_free(ccp->clut); + } else if (ccp->clut != NULL) { + gdk_colors_free(ccp->colormap, ccp->clut, ccp->num_colors, 0); + g_free(ccp->clut); + } + + if (ccp->cmap != NULL) + g_free(ccp->cmap); + + if (ccp->need_to_free_colormap) + gdk_colormap_destroy(ccp->colormap); + + /* free any palette that has been associated with this GdkColorContext */ + + init_palette(ccp); + + if (ccp->color_hash) { + g_hash_table_foreach(ccp->color_hash, + free_hash_entry, + NULL); + g_hash_table_destroy(ccp->color_hash); + } + + g_free(cc); +} + +gulong +gdk_color_context_get_pixel(GdkColorContext *cc, + gushort red, + gushort green, + gushort blue, + gint *failed) +{ + GdkColorContextPrivate *ccp; + + g_assert(cc != NULL); + g_assert(failed != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + *failed = FALSE; + + switch (ccp->mode) { + case GDK_CC_MODE_BW: { + gdouble value; + + red <<= 8; + green <<= 8; + blue <<= 8; + + value = red / 65535.0 * 0.30 + + green / 65535.0 * 0.59 + + blue / 65535.0 * 0.11; + + if (value > 0.5) + return ccp->white_pixel; + + return ccp->black_pixel; + } + + case GDK_CC_MODE_MY_GRAY: { + gulong ired, igreen, iblue; + + red <<= 8; + green <<= 8; + blue <<= 8; + + red = red * 0.30 + green * 0.59 + blue * 0.11; + green = 0; + blue = 0; + + if ((ired = red * (ccp->std_cmap.red_max + 1) / 0xffff) + > ccp->std_cmap.red_max) + ired = ccp->std_cmap.red_max; + + ired *= ccp->std_cmap.red_mult; + + if ((igreen = green * (ccp->std_cmap.green_max + 1) / 0xffff) + > ccp->std_cmap.green_max) + igreen = ccp->std_cmap.green_max; + + igreen *= ccp->std_cmap.green_mult; + + if ((iblue = blue * (ccp->std_cmap.blue_max + 1) / 0xffff) + > ccp->std_cmap.blue_max) + iblue = ccp->std_cmap.blue_max; + + iblue *= ccp->std_cmap.blue_mult; + + if (ccp->clut != NULL) + return ccp->clut[ccp->std_cmap.base_pixel + ired + igreen + iblue]; + + return ccp->std_cmap.base_pixel + ired + igreen + iblue; + } + + case GDK_CC_MODE_TRUE: { + gulong ired, igreen, iblue; + + red <<= 8; + green <<= 8; + blue <<= 8; + + if (ccp->clut == NULL) { + red >>= 16 - ccp->bits.red; + green >>= 16 - ccp->bits.green; + blue >>= 16 - ccp->bits.blue; + + ired = (red << ccp->shifts.red) & ccp->masks.red; + igreen = (green << ccp->shifts.green) & ccp->masks.green; + iblue = (blue << ccp->shifts.blue) & ccp->masks.blue; + + return ired | igreen | iblue; + } + + ired = ccp->clut[red * ccp->max_entry / 65535] & ccp->masks.red; + igreen = ccp->clut[green * ccp->max_entry / 65535] & ccp->masks.green; + iblue = ccp->clut[blue * ccp->max_entry / 65535] & ccp->masks.blue; + + return ired | igreen | iblue; + } + + case GDK_CC_MODE_PALETTE: + return gdk_color_context_get_pixel_from_palette(cc, &red, &green, &blue, failed); + + case GDK_CC_MODE_STD_CMAP: + default: { + GdkColor color; + GdkColor *result; + + red <<= 8; + green <<= 8; + blue <<= 8; + + color.red = red; + color.green = green; + color.blue = blue; + + result = g_hash_table_lookup(ccp->color_hash, &color); + + if (!result) { + color.red = red; + color.green = green; + color.blue = blue; + color.pixel = 0; + + if (!gdk_color_alloc(ccp->colormap, &color)) + *failed = TRUE; + else { + GdkColor *cnew; + + /* XXX: the following comment comes directly from + * XCC.c. I don't know if it is relevant for + * gdk_color_alloc() as it is for XAllocColor() + * - Federico + */ + /* + * I can't figure this out entirely, but it *is* possible + * that XAllocColor succeeds, even if the number of + * allocations we've made exceeds the number of available + * colors in the current colormap. And therefore it + * might be necessary for us to resize the CLUT. + */ + + if (ccp->num_allocated == ccp->max_colors) { + ccp->max_colors *= 2; + + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixel: " + "resizing CLUT to %i entries", + ccp->max_colors); + + ccp->clut = g_realloc(ccp->clut, + ccp->max_colors * sizeof(gulong)); + } + + /* Key and value are the same color structure */ + + cnew = g_new(GdkColor, 1); + *cnew = color; + g_hash_table_insert(ccp->color_hash, cnew, cnew); + + ccp->clut[ccp->num_allocated] = color.pixel; + ccp->num_allocated++; + return color.pixel; + } + } + + return result->pixel; + } + } +} + +void +gdk_color_context_get_pixels(GdkColorContext *cc, + gushort *reds, + gushort *greens, + gushort *blues, + gint ncolors, + gulong *colors, + gint *nallocated) +{ + GdkColorContextPrivate *ccp; + gint i, k, idx; + gint cmapsize, ncols = 0, nopen = 0, counter = 0; + gint bad_alloc = FALSE; + gint failed[MAX_IMAGE_COLORS], allocated[MAX_IMAGE_COLORS]; + GdkColor defs[MAX_IMAGE_COLORS], cmap[MAX_IMAGE_COLORS]; + gint exact_col = 0, subst_col = 0, close_col = 0, black_col = 0; + + g_assert(cc != NULL); + g_assert(reds != NULL); + g_assert(greens != NULL); + g_assert(blues != NULL); + g_assert(colors != NULL); + g_assert(nallocated != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + memset(defs, 0, MAX_IMAGE_COLORS * sizeof(GdkColor)); + memset(failed, 0, MAX_IMAGE_COLORS * sizeof(gint)); + memset(allocated, 0, MAX_IMAGE_COLORS * sizeof(gint)); + + /* Will only have a value if used by the progressive image loader */ + + ncols = *nallocated; + + *nallocated = 0; + + /* First allocate all pixels */ + + for (i = 0; i < ncolors; i++) { + /* colors[i] is only zero if the pixel at that location hasn't + * been allocated yet. This is a sanity check required for proper + * color allocation by the progressive image loader + */ + + if (colors[i] == 0) { + defs[i].red = reds[i]; + defs[i].green = greens[i]; + defs[i].blue = blues[i]; + + colors[i] = gdk_color_context_get_pixel(cc, reds[i], greens[i], blues[i], + &bad_alloc); + + /* successfully allocated, store it */ + + if (!bad_alloc) { + defs[i].pixel = colors[i]; + allocated[ncols++] = colors[i]; + } else + failed[nopen++] = i; + } + } + + *nallocated = ncols; + + /* all colors available, all done */ + + if ((ncols == ncolors) || (nopen == 0)) { + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixels: got all %i colors; " + "(%i colors allocated so far", ncolors, ccp->num_allocated); + + return; + } + + /* The fun part. We now try to allocate the colors we couldn't allocate + * directly. The first step will map a color onto its nearest color + * that has been allocated (either by us or someone else). If any colors + * remain unallocated, we map these onto the colors that we have allocated + * ourselves. + */ + + /* read up to MAX_IMAGE_COLORS colors of the current colormap */ + + cmapsize = MIN(ccp->num_colors, MAX_IMAGE_COLORS); + + /* see if the colormap has any colors to read */ + + if (cmapsize < 0) { + g_warning("gdk_color_context_get_pixels: oops! no colors available, " + "your images will look *really* ugly."); + + return; + } + +#ifdef DEBUG + exact_col = ncols; +#endif + + /* initialize pixels */ + + for (i = 0; i < cmapsize; i++) { + cmap[i].pixel = i; + cmap[i].red = cmap[i].green = cmap[i].blue = 0; + } + + /* read the colormap */ + + my_x_query_colors(ccp->colormap, cmap, cmapsize); + + /* speedup: downscale here instead of in the matching code */ + + for (i = 0; i < cmapsize; i++) { + cmap[i].red >>= 8; + cmap[i].green >>= 8; + cmap[i].blue >>= 8; + } + + /* get a close match for any unallocated colors */ + + counter = nopen; + nopen = 0; + idx = 0; + + do { + gint d, j, mdist, close, ri, gi, bi; + gint rd, gd, bd; + + i = failed[idx]; + + mdist = 1000000; + close = -1; + + /* Store these vals. Small performance increase as this skips three + * indexing operations in the loop code. + */ + + ri = reds[i]; + gi = greens[i]; + bi = blues[i]; + + /* Walk all colors in the colormap and see which one is the + * closest. Uses plain least squares. + */ + + for (j = 0; (j < cmapsize) && (mdist != 0); j++) { + rd = ri - cmap[j].red; + gd = gi - cmap[j].green; + bd = bi - cmap[j].blue; + + d = rd * rd + gd * gd + bd * bd; + + if (d < mdist) { + close = j; + mdist = d; + } + } + + if (close != -1) { + rd = cmap[close].red; + gd = cmap[close].green; + bd = cmap[close].blue; + + /* allocate */ + + colors[i] = gdk_color_context_get_pixel(cc, rd, gd, bd, &bad_alloc); + + /* store */ + + if (!bad_alloc) { + defs[i] = cmap[close]; + defs[i].pixel = colors[i]; + allocated[ncols++] = colors[i]; +#ifdef DEBUG + close_col++; +#endif + } else + failed[nopen++] = i; + } else + failed[nopen++] = i; + /* deal with in next stage if allocation failed */ + } while (++idx < counter); + + *nallocated = ncols; + + /* This is the maximum no. of allocated colors. See also the nopen == 0 + * note above. + */ + + if ((ncols == ncolors) || (nopen == 0)) { + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixels: got %i colors, %i exact and " + "%i close (%i colors allocated so far)", + ncolors, exact_col, close_col, ccp->num_allocated); + + return; + } + + /* Now map any remaining unallocated pixels into the colors we did get */ + + idx = 0; + + do { + gint d, mdist, close, ri, gi, bi; + gint j, rd, gd, bd; + + i = failed[idx]; + + mdist = 1000000; + close = -1; + + /* store */ + + ri = reds[i]; + gi = greens[i]; + bi = blues[i]; + + /* search allocated colors */ + + for (j = 0; (j < ncols) && (mdist != 0); j++) { + k = allocated[j]; + + rd = ri - defs[k].red; + gd = gi - defs[k].green; + bd = bi - defs[k].blue; + + d = rd * rd + gd * gd + bd * bd; + + if (d < mdist) { + close = k; + mdist = d; + } + } + + if (close < 0) { + /* too bad, map to black */ + + defs[i].pixel = ccp->black_pixel; + defs[i].red = defs[i].green = defs[i].blue = 0; +#ifdef DEBUG + black_col++; +#endif + } else { + defs[i] = defs[close]; +#ifdef DEBUG + subst_col++; +#endif + } + + colors[i] = defs[i].pixel; + } while (++idx < nopen); + + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixels: got %i colors, %i exact, %i close, " + "%i substituted, %i to black (%i colors allocated so far)", + ncolors, exact_col, close_col, subst_col, black_col, ccp->num_allocated); +} + +void +gdk_color_context_get_pixels_incremental(GdkColorContext *cc, + gushort *reds, + gushort *greens, + gushort *blues, + gint ncolors, + gint *used, + gulong *colors, + gint *nallocated) +{ + GdkColorContextPrivate *ccp; + gint i, k, idx; + gint cmapsize, ncols = 0, nopen = 0, counter = 0; + gint bad_alloc = FALSE; + gint failed[MAX_IMAGE_COLORS], allocated[MAX_IMAGE_COLORS]; + GdkColor defs[MAX_IMAGE_COLORS], cmap[MAX_IMAGE_COLORS]; + gint exact_col = 0, subst_col = 0, close_col = 0, black_col = 0; + + g_assert(cc != NULL); + g_assert(reds != NULL); + g_assert(greens != NULL); + g_assert(blues != NULL); + g_assert(used != NULL); + g_assert(colors != NULL); + g_assert(nallocated != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + memset(defs, 0, MAX_IMAGE_COLORS * sizeof(GdkColor)); + memset(failed, 0, MAX_IMAGE_COLORS * sizeof(gint)); + memset(allocated, 0, MAX_IMAGE_COLORS * sizeof(gint)); + + /* Will only have a value if used by the progressive image loader */ + + ncols = *nallocated; + + *nallocated = 0; + + /* First allocate all pixels */ + + for (i = 0; i < ncolors; i++) { + /* used[i] is only -1 if the pixel at that location hasn't + * been allocated yet. This is a sanity check required for proper + * color allocation by the progressive image loader. + * When colors[i] == 0 it indicates the slot is available for + * allocation. + */ + + if (used[i] != FALSE) { + if (colors[i] == 0) { + defs[i].red = reds[i]; + defs[i].green = greens[i]; + defs[i].blue = blues[i]; + + colors[i] = gdk_color_context_get_pixel(cc, reds[i], greens[i], blues[i], &bad_alloc); + + /* successfully allocated, store it */ + + if (!bad_alloc) { + defs[i].pixel = colors[i]; + allocated[ncols++] = colors[i]; + } else + failed[nopen++] = i; + } +#ifdef DEBUG + else + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixels_incremental: " + "pixel at slot %i already allocated, skipping", i); +#endif + } + } + + *nallocated = ncols; + + if ((ncols == ncolors) || (nopen == 0)) { + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixels_incremental: got all %i colors " + "(%i colors allocated so far)", + ncolors, ccp->num_allocated); + + return; + } + + cmapsize = MIN(ccp->num_colors, MAX_IMAGE_COLORS); + + if (cmapsize < 0) { + g_warning("gdk_color_context_get_pixels_incremental: oops! " + "No colors available images will look *really* ugly."); + return; + } + +#ifdef DEBUG + exact_col = ncols; +#endif + + /* initialize pixels */ + + for (i = 0; i < cmapsize; i++) { + cmap[i].pixel = i; + cmap[i].red = cmap[i].green = cmap[i].blue = 0; + } + + /* read and downscale */ + + my_x_query_colors(ccp->colormap, cmap, cmapsize); + + for (i = 0; i < cmapsize; i++) { + cmap[i].red >>= 8; + cmap[i].green >>= 8; + cmap[i].blue >>= 8; + } + + /* now match any unallocated colors */ + + counter = nopen; + nopen = 0; + idx = 0; + + do { + gint d, j, mdist, close, ri, gi, bi; + gint rd, gd, bd; + + i = failed[idx]; + + mdist = 1000000; + close = -1; + + /* store */ + + ri = reds[i]; + gi = greens[i]; + bi = blues[i]; + + for (j = 0; (j < cmapsize) && (mdist != 0); j++) { + rd = ri - cmap[j].red; + gd = gi - cmap[j].green; + bd = bi - cmap[j].blue; + + d = rd * rd + gd * gd + bd * bd; + + if (d < mdist) { + close = j; + mdist = d; + } + } + + if (close != -1) { + rd = cmap[close].red; + gd = cmap[close].green; + bd = cmap[close].blue; + + /* allocate */ + + colors[i] = gdk_color_context_get_pixel(cc, rd, gd, bd, &bad_alloc); + + /* store */ + + if (!bad_alloc) { + defs[i] = cmap[close]; + defs[i].pixel = colors[i]; + allocated[ncols++] = colors[i]; +#ifdef DEBUG + close_col++; +#endif + } else + failed[nopen++] = i; + } else + failed[nopen++] = i; + /* deal with in next stage if allocation failed */ + } while (++idx < counter); + + *nallocated = ncols; + + if ((ncols == ncolors) || (nopen == 0)) { + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixels_incremental: " + "got %i colors, %i exact and %i close " + "(%i colors allocated so far)", + ncolors, exact_col, close_col, ccp->num_allocated); + + return; + } + + /* map remaining unallocated pixels into colors we did get */ + + idx = 0; + + do { + gint d, mdist, close, ri, gi, bi; + gint j, rd, gd, bd; + + i = failed[idx]; + + mdist = 1000000; + close = -1; + + ri = reds[i]; + gi = greens[i]; + bi = blues[i]; + + /* search allocated colors */ + + for (j = 0; (j < ncols) && (mdist != 0); j++) { + k = allocated[j]; + + /* downscale */ + + rd = ri - defs[k].red; + gd = gi - defs[k].green; + bd = bi - defs[k].blue; + + d = rd * rd + gd * gd + bd * bd; + + if (d < mdist) { + close = k; + mdist = d; + } + } + + if (close < 0) { + /* too bad, map to black */ + + defs[i].pixel = ccp->black_pixel; + defs[i].red = defs[i].green = defs[i].blue = 0; +#ifdef DEBUG + black_col++; +#endif + } else { + defs[i] = defs[close]; +#ifdef DEBUG + subst_col++; +#endif + } + + colors[i] = defs[i].pixel; + } while (++idx < nopen); + + if (gdk_debug_level >= 1) + g_print("gdk_color_context_get_pixels_incremental: " + "got %i colors, %i exact, %i close, %i substituted, %i to black " + "(%i colors allocated so far)", + ncolors, exact_col, close_col, subst_col, black_col, ccp->num_allocated); +} + +gint +gdk_color_context_get_num_colors(GdkColorContext *cc) +{ + g_assert(cc != NULL); + + return ((GdkColorContextPrivate *) cc)->num_colors; +} + +gint +gdk_color_context_query_color(GdkColorContext *cc, + GdkColor *color) +{ + return gdk_color_context_query_colors(cc, color, 1); +} + +gint +gdk_color_context_query_colors(GdkColorContext *cc, + GdkColor *colors, + gint num_colors) +{ + GdkColorContextPrivate *ccp; + gint i; + GdkColor *tc; + + g_assert(cc != NULL); + g_assert(colors != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + switch (ccp->mode) { + case GDK_CC_MODE_BW: + for (i = 0, tc = colors; i < num_colors; i++, tc++) { + if (tc->pixel == ccp->white_pixel) + tc->red = tc->green = tc->blue = 65535; + else + tc->red = tc->green = tc->blue = 0; + } + break; + + case GDK_CC_MODE_TRUE: + if (ccp->clut == NULL) + for (i = 0, tc = colors; i < num_colors; i++, tc++) { + tc->red = (tc->pixel & ccp->masks.red) * 65535 / ccp->masks.red; + tc->green = (tc->pixel & ccp->masks.green) * 65535 / ccp->masks.green; + tc->blue = (tc->pixel & ccp->masks.blue) * 65535 / ccp->masks.blue; + } + else { + my_x_query_colors(ccp->colormap, colors, num_colors); + return 1; + } + break; + + case GDK_CC_MODE_STD_CMAP: + default: + if (ccp->cmap == NULL) { + my_x_query_colors(ccp->colormap, colors, num_colors); + return 1; + } else { + gint first, last, half; + gulong half_pixel; + + for (i = 0, tc = colors; i < num_colors; i++) { + first = 0; + last = ccp->num_colors - 1; + + while (first <= last) { + half = (first + last) / 2; + half_pixel = ccp->cmap[half].pixel; + + if (tc->pixel == half_pixel) { + tc->red = ccp->cmap[half].red; + tc->green = ccp->cmap[half].green; + tc->blue = ccp->cmap[half].blue; + first = last + 1; /* false break */ + } else { + if (tc->pixel > half_pixel) + first = half + 1; + else + last = half - 1; + } + } + } + return 1; + } + break; + } + return 1; +} + +gint +gdk_color_context_add_palette(GdkColorContext *cc, + GdkColor *palette, + gint num_palette) +{ + GdkColorContextPrivate *ccp; + gint i, j, erg; + gushort r, g, b; + gulong pixel[1]; + + g_assert(cc != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + /* initialize this palette (will also erase previous palette as well) */ + + init_palette(ccp); + + /* restore previous mode if we aren't adding a new palette */ + + if (num_palette == 0) { + /* GDK_CC_MODE_STD_CMAP uses a hash table, so we'd better initialize one */ + + /* XXX: here, the hash table is already initialized */ + + return 0; + } + + /* Initialize a hash table for this palette (we need one for allocating + * the pixels in the palette using the current settings) + */ + + if (ccp->color_hash == NULL) + ccp->color_hash = g_hash_table_new(hash_color, compare_colors); + + /* copy incoming palette */ + + ccp->palette = g_new0(GdkColor, num_palette); + + j = 0; + + for (i = 0; i < num_palette; i++) { + erg = 0; + pixel[0] = 0; + + /* try to allocate this color */ + + r = palette[i].red; + g = palette[i].green; + b = palette[i].blue; + + gdk_color_context_get_pixels(cc, &r, &g, &b, 1, pixel, &erg); + + /* only store if we succeed */ + + if (erg) { + /* store in palette */ + + ccp->palette[j].red = r; + ccp->palette[j].green = g; + ccp->palette[j].blue = b; + ccp->palette[j].pixel = pixel[0]; + + /* move to next slot */ + + j++; + } + } + + /* resize to fit */ + + if (j != num_palette) + ccp->palette = g_realloc(ccp->palette, j * sizeof(GdkColor)); + + /* clear the hash table, we don't use it when dithering */ + + if (ccp->color_hash) { + g_hash_table_destroy(ccp->color_hash); + ccp->color_hash = NULL; + } + + /* store real palette size */ + + ccp->num_palette = j; + + /* switch to palette mode */ + + ccp->mode = GDK_CC_MODE_PALETTE; + + /* sort palette */ + + qsort(ccp->palette, ccp->num_palette, sizeof(GdkColor), pixel_sort); + + ccp->fast_dither = NULL; + + return j; +} + +void +gdk_color_context_init_dither(GdkColorContext *cc) +{ + GdkColorContextPrivate *ccp; + gint rr, gg, bb, err, erg, erb; + gint success = FALSE; + + g_assert(cc != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + /* now we can initialize the fast dither matrix */ + + if(ccp->fast_dither == NULL) + ccp->fast_dither = g_new(GdkColorContextDither, 1); + + /* Fill it. We ignore unsuccessful allocations, they are just mapped + * to black instead */ + + for (rr = 0; rr < 32; rr++) + for (gg = 0; gg < 32; gg++) + for (bb = 0; bb < 32; bb++) { + err = (rr << 3) | (rr >> 2); + erg = (gg << 3) | (gg >> 2); + erb = (bb << 3) | (bb >> 2); + + ccp->fast_dither->fast_rgb[rr][gg][bb] = + gdk_color_context_get_index_from_palette(cc, &err, &erg, &erb, &success); + ccp->fast_dither->fast_err[rr][gg][bb] = err; + ccp->fast_dither->fast_erg[rr][gg][bb] = erg; + ccp->fast_dither->fast_erb[rr][gg][bb] = erb; + } +} + +void +gdk_color_context_free_dither(GdkColorContext *cc) +{ + GdkColorContextPrivate *ccp; + + g_assert(cc != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + if (ccp->fast_dither) + g_free(ccp->fast_dither); + + ccp->fast_dither = NULL; +} + +gulong +gdk_color_context_get_pixel_from_palette(GdkColorContext *cc, + gushort *red, + gushort *green, + gushort *blue, + gint *failed) +{ + GdkColorContextPrivate *ccp; + gulong pixel = 0; + gint dif, dr, dg, db, j = -1; + gint mindif = 0x7fffffff; + gint err = 0, erg = 0, erb = 0; + gint i; + + g_assert(cc != NULL); + g_assert(red != NULL); + g_assert(green != NULL); + g_assert(blue != NULL); + g_assert(failed != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + *failed = FALSE; + + for (i = 0; i < ccp->num_palette; i++) { + dr = *red - ccp->palette[i].red; + dg = *green - ccp->palette[i].green; + db = *blue - ccp->palette[i].blue; + + dif = dr * dr + dg * dg + db * db; + + if (dif < mindif) { + mindif = dif; + j = i; + pixel = ccp->palette[i].pixel; + err = dr; + erg = dg; + erb = db; + + if (mindif == 0) + break; + } + } + + /* we failed to map onto a color */ + + if (j == -1) + *failed = TRUE; + else { + *red = ABS(err); + *green = ABS(erg); + *blue = ABS(erb); + } + + return pixel; +} + +guchar +gdk_color_context_get_index_from_palette(GdkColorContext *cc, + gint *red, + gint *green, + gint *blue, + gint *failed) +{ + GdkColorContextPrivate *ccp; + gint dif, dr, dg, db, j = -1; + gint mindif = 0x7fffffff; + gint err = 0, erg = 0, erb = 0; + gint i; + + g_assert(cc != NULL); + g_assert(red != NULL); + g_assert(green != NULL); + g_assert(blue != NULL); + g_assert(failed != NULL); + + ccp = (GdkColorContextPrivate *) cc; + + *failed = FALSE; + + for (i = 0; i < ccp->num_palette; i++) { + dr = *red - ccp->palette[i].red; + dg = *green - ccp->palette[i].green; + db = *blue - ccp->palette[i].blue; + + dif = dr * dr + dg * dg + db * db; + + if (dif < mindif) { + mindif = dif; + j = i; + err = dr; + erg = dg; + erb = db; + + if (mindif == 0) + break; + } + } + + /* we failed to map onto a color */ + + if (j == -1) { + *failed = TRUE; + j = 0; + } else { + /* return error fractions */ + + *red = err; + *green = erg; + *blue = erb; + } + + return j; +} |