1645 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1645 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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(GdkColorContext *cc)
 | |
| {
 | |
| 	gint i;
 | |
| 	GdkColorContextPrivate *ccp = (GdkColorContextPrivate *) cc;
 | |
| 	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] : ccp->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(GdkColorContext *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(GdkColorContext *cc)
 | |
| {
 | |
| 	GdkColorContextPrivate *ccp = (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? */
 | |
| 	ccp->std_cmap.colormap = GDK_COLORMAP_XCOLORMAP(cc->colormap);
 | |
| 	ccp->std_cmap.base_pixel = 0;
 | |
| 	ccp->std_cmap.red_max = cc->num_colors - 1;
 | |
| 	ccp->std_cmap.green_max = 0;
 | |
| 	ccp->std_cmap.blue_max = 0;
 | |
| 	ccp->std_cmap.red_mult = 1;
 | |
| 	ccp->std_cmap.green_mult = 0;
 | |
| 	ccp->std_cmap.blue_mult = 0;
 | |
| 
 | |
| 	cc->white_pixel = WhitePixel(ccp->xdisplay, gdk_screen);
 | |
| 	cc->black_pixel = BlackPixel(ccp->xdisplay, gdk_screen);
 | |
| 
 | |
| 	query_colors(cc);
 | |
| 
 | |
| 	cc->mode = GDK_CC_MODE_MY_GRAY;
 | |
| }
 | |
| 
 | |
| static void
 | |
| init_color(GdkColorContext *cc)
 | |
| {
 | |
| 	GdkColorContextPrivate *ccp = (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;
 | |
| 
 | |
| 	ccp->std_cmap.red_max    = cubeval - 1;
 | |
| 	ccp->std_cmap.green_max  = cubeval - 1;
 | |
| 	ccp->std_cmap.blue_max   = cubeval - 1;
 | |
| 	ccp->std_cmap.red_mult   = cubeval * cubeval;
 | |
| 	ccp->std_cmap.green_mult = cubeval;
 | |
| 	ccp->std_cmap.blue_mult  = 1;
 | |
| 	ccp->std_cmap.base_pixel = 0;
 | |
| 
 | |
| 	cc->white_pixel = WhitePixel(ccp->xdisplay, gdk_screen);
 | |
| 	cc->black_pixel = BlackPixel(ccp->xdisplay, gdk_screen);
 | |
| 	cc->num_colors = DisplayCells(ccp->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(GdkColorContext *cc)
 | |
| {
 | |
| 	GdkColorContextPrivate *ccp = (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(ccp->xdisplay, gdk_screen);
 | |
| 	cc->black_pixel = BlackPixel(ccp->xdisplay, gdk_screen);
 | |
| }
 | |
| 
 | |
| static void
 | |
| init_direct_color(GdkColorContext *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(GdkColorContext *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)
 | |
| {
 | |
| 	GdkColorContextPrivate *ccp;
 | |
| 	gint use_private_colormap = FALSE; /* XXX: maybe restore full functionality later? */
 | |
| 	GdkColorContext *cc;
 | |
| 	gint retry_count;
 | |
| 	GdkColormap *default_colormap;
 | |
| 
 | |
| 	g_assert(visual != NULL);
 | |
| 	g_assert(colormap != NULL);
 | |
| 	
 | |
| 	cc = g_new(GdkColorContext, 1);
 | |
| 	ccp = (GdkColorContextPrivate *) cc;
 | |
| 	ccp->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 *ccp;
 | |
| 	GdkColorContext *cc;
 | |
| 
 | |
| 	g_assert(visual != NULL);
 | |
| 	g_assert(colormap != NULL);
 | |
| 
 | |
| 	cc = g_new(GdkColorContext, 1);
 | |
| 	ccp = (GdkColorContextPrivate *) cc;
 | |
| 	ccp->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)
 | |
| {
 | |
| 	g_assert(cc != NULL);
 | |
| 
 | |
| 	if ((cc->visual->type == GDK_VISUAL_STATIC_COLOR)
 | |
| 	    || (cc->visual->type == GDK_VISUAL_PSEUDO_COLOR)) {
 | |
| 		gdk_colors_free(cc->colormap, cc->clut, cc->num_allocated, 0);
 | |
| 		g_free(cc->clut);
 | |
| 	} else if (cc->clut != NULL) {
 | |
| 		gdk_colors_free(cc->colormap, cc->clut, cc->num_colors, 0);
 | |
| 		g_free(cc->clut);
 | |
| 	}
 | |
| 
 | |
| 	if (cc->cmap != NULL)
 | |
| 		g_free(cc->cmap);
 | |
| 
 | |
| 	if (cc->need_to_free_colormap)
 | |
| 		gdk_colormap_unref(cc->colormap);
 | |
| 
 | |
| 	/* free any palette that has been associated with this GdkColorContext */
 | |
| 
 | |
| 	init_palette(cc);
 | |
| 
 | |
| 	if (cc->color_hash) {
 | |
| 		g_hash_table_foreach(cc->color_hash,
 | |
| 				     free_hash_entry,
 | |
| 				     NULL);
 | |
| 		g_hash_table_destroy(cc->color_hash);
 | |
| 	}
 | |
| 
 | |
| 	g_free(cc);
 | |
| }
 | |
| 
 | |
| gulong
 | |
| gdk_color_context_get_pixel(GdkColorContext *cc,
 | |
| 			    gushort          red,
 | |
| 			    gushort          green,
 | |
| 			    gushort          blue,
 | |
| 			    gint            *failed)
 | |
| {
 | |
| 	GdkColorContextPrivate *ccp = (GdkColorContextPrivate *) cc;
 | |
| 	g_assert(cc != NULL);
 | |
| 	g_assert(failed != NULL);
 | |
| 
 | |
| 	*failed = FALSE;
 | |
| 
 | |
| 	switch (cc->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 cc->white_pixel;
 | |
| 
 | |
| 			return cc->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 (cc->clut != NULL)
 | |
| 				return cc->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 (cc->clut == NULL) {
 | |
| 				red   >>= 16 - cc->bits.red;
 | |
| 				green >>= 16 - cc->bits.green;
 | |
| 				blue  >>= 16 - cc->bits.blue;
 | |
| 
 | |
| 				ired   = (red << cc->shifts.red) & cc->masks.red;
 | |
| 				igreen = (green << cc->shifts.green) & cc->masks.green;
 | |
| 				iblue  = (blue << cc->shifts.blue) & cc->masks.blue;
 | |
| 
 | |
| 				return ired | igreen | iblue;
 | |
| 			}
 | |
| 
 | |
| 			ired   = cc->clut[red * cc->max_entry / 65535] & cc->masks.red;
 | |
| 			igreen = cc->clut[green * cc->max_entry / 65535] & cc->masks.green;
 | |
| 			iblue  = cc->clut[blue * cc->max_entry / 65535] & cc->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(cc->color_hash, &color);
 | |
| 
 | |
| 			if (!result) {
 | |
| 				color.red   = red;
 | |
| 				color.green = green;
 | |
| 				color.blue  = blue;
 | |
| 				color.pixel = 0;
 | |
| 
 | |
| 				if (!gdk_color_alloc(cc->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 (cc->num_allocated == cc->max_colors) {
 | |
| 						cc->max_colors *= 2;
 | |
| 
 | |
| 						if (gdk_debug_level >= 1)
 | |
| 							g_print("gdk_color_context_get_pixel: "
 | |
| 								"resizing CLUT to %i entries",
 | |
| 								cc->max_colors);
 | |
| 
 | |
| 						cc->clut = g_realloc(cc->clut,
 | |
| 								      cc->max_colors * sizeof(gulong));
 | |
| 					}
 | |
| 
 | |
| 					/* Key and value are the same color structure */
 | |
| 
 | |
| 					cnew = g_new(GdkColor, 1);
 | |
| 					*cnew = color;
 | |
| 					g_hash_table_insert(cc->color_hash, cnew, cnew);
 | |
| 
 | |
| 					cc->clut[cc->num_allocated] = color.pixel;
 | |
| 					cc->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)
 | |
| {
 | |
| 	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);
 | |
| 
 | |
| 	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, cc->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(cc->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(cc->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, cc->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 = cc->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, cc->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)
 | |
| {
 | |
| 	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);
 | |
| 
 | |
| 	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, cc->num_allocated);
 | |
| 
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	cmapsize = MIN(cc->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(cc->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, cc->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 = cc->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, cc->num_allocated);
 | |
| }
 | |
| 
 | |
| 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)
 | |
| {
 | |
| 	gint i;
 | |
| 	GdkColor *tc;
 | |
| 	
 | |
| 	g_assert(cc != NULL);
 | |
| 	g_assert(colors != NULL);
 | |
| 
 | |
| 	switch (cc->mode) {
 | |
| 		case GDK_CC_MODE_BW:
 | |
| 			for (i = 0, tc = colors; i < num_colors; i++, tc++) {
 | |
| 				if (tc->pixel == cc->white_pixel)
 | |
| 					tc->red = tc->green = tc->blue = 65535;
 | |
| 				else
 | |
| 					tc->red = tc->green = tc->blue = 0;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		case GDK_CC_MODE_TRUE:
 | |
| 			if (cc->clut == NULL)
 | |
| 				for (i = 0, tc = colors; i < num_colors; i++, tc++) {
 | |
| 					tc->red   = (tc->pixel & cc->masks.red) * 65535 / cc->masks.red;
 | |
| 					tc->green = (tc->pixel & cc->masks.green) * 65535 / cc->masks.green;
 | |
| 					tc->blue  = (tc->pixel & cc->masks.blue) * 65535 / cc->masks.blue;
 | |
| 				}
 | |
| 			else {
 | |
| 				my_x_query_colors(cc->colormap, colors, num_colors);
 | |
| 				return 1;
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 		case GDK_CC_MODE_STD_CMAP:
 | |
| 		default:
 | |
| 			if (cc->cmap == NULL) {
 | |
| 				my_x_query_colors(cc->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 = cc->num_colors - 1;
 | |
| 
 | |
| 					while (first <= last) {
 | |
| 						half = (first + last) / 2;
 | |
| 						half_pixel = cc->cmap[half].pixel;
 | |
| 
 | |
| 						if (tc->pixel == half_pixel) {
 | |
| 							tc->red   = cc->cmap[half].red;
 | |
| 							tc->green = cc->cmap[half].green;
 | |
| 							tc->blue  = cc->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)
 | |
| {
 | |
| 	gint i, j, erg;
 | |
| 	gushort r, g, b;
 | |
| 	gulong pixel[1];
 | |
| 
 | |
| 	g_assert(cc != NULL);
 | |
| 
 | |
| 	/* initialize this palette (will also erase previous palette as well) */
 | |
| 
 | |
| 	init_palette(cc);
 | |
| 
 | |
| 	/* 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 (cc->color_hash == NULL)
 | |
| 		cc->color_hash = g_hash_table_new(hash_color, compare_colors);
 | |
| 
 | |
| 	/* copy incoming palette */
 | |
| 
 | |
| 	cc->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 */
 | |
| 
 | |
| 			cc->palette[j].red   = r;
 | |
| 			cc->palette[j].green = g;
 | |
| 			cc->palette[j].blue  = b;
 | |
| 			cc->palette[j].pixel = pixel[0];
 | |
| 
 | |
| 			/* move to next slot */
 | |
| 
 | |
| 			j++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* resize to fit */
 | |
| 
 | |
| 	if (j != num_palette)
 | |
| 		cc->palette = g_realloc(cc->palette, j * sizeof(GdkColor));
 | |
| 
 | |
| 	/* clear the hash table, we don't use it when dithering */
 | |
| 
 | |
| 	if (cc->color_hash) {
 | |
| 		g_hash_table_destroy(cc->color_hash);
 | |
| 		cc->color_hash = NULL;
 | |
| 	}
 | |
| 
 | |
| 	/* store real palette size */
 | |
| 
 | |
| 	cc->num_palette = j;
 | |
| 
 | |
| 	/* switch to palette mode */
 | |
| 
 | |
| 	cc->mode = GDK_CC_MODE_PALETTE;
 | |
| 
 | |
| 	/* sort palette */
 | |
| 
 | |
| 	qsort(cc->palette, cc->num_palette, sizeof(GdkColor), pixel_sort);
 | |
| 
 | |
| 	cc->fast_dither = NULL;
 | |
| 
 | |
| 	return j;
 | |
| }
 | |
| 
 | |
| void
 | |
| gdk_color_context_init_dither(GdkColorContext *cc)
 | |
| {
 | |
| 	gint rr, gg, bb, err, erg, erb;
 | |
| 	gint success = FALSE;
 | |
| 
 | |
| 	g_assert(cc != NULL);
 | |
| 
 | |
| 	/* now we can initialize the fast dither matrix */
 | |
| 
 | |
| 	if(cc->fast_dither == NULL)
 | |
| 		cc->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);
 | |
| 
 | |
| 				cc->fast_dither->fast_rgb[rr][gg][bb] =
 | |
| 					gdk_color_context_get_index_from_palette(cc, &err, &erg, &erb, &success);
 | |
| 				cc->fast_dither->fast_err[rr][gg][bb] = err;
 | |
| 				cc->fast_dither->fast_erg[rr][gg][bb] = erg;
 | |
| 				cc->fast_dither->fast_erb[rr][gg][bb] = erb;
 | |
| 			}
 | |
| }
 | |
| 
 | |
| void
 | |
| gdk_color_context_free_dither(GdkColorContext *cc)
 | |
| {
 | |
| 	g_assert(cc != NULL);
 | |
| 
 | |
| 	if (cc->fast_dither)
 | |
| 		g_free(cc->fast_dither);
 | |
| 
 | |
| 	cc->fast_dither = NULL;
 | |
| }
 | |
| 
 | |
| gulong
 | |
| gdk_color_context_get_pixel_from_palette(GdkColorContext *cc,
 | |
| 					 gushort         *red,
 | |
| 					 gushort         *green,
 | |
| 					 gushort         *blue,
 | |
| 					 gint            *failed)
 | |
| {
 | |
| 	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);
 | |
| 
 | |
| 	*failed = FALSE;
 | |
| 
 | |
| 	for (i = 0; i < cc->num_palette; i++) {
 | |
| 		dr = *red - cc->palette[i].red;
 | |
| 		dg = *green - cc->palette[i].green;
 | |
| 		db = *blue - cc->palette[i].blue;
 | |
| 
 | |
| 		dif = dr * dr + dg * dg + db * db;
 | |
| 		
 | |
| 		if (dif < mindif) {
 | |
| 			mindif = dif;
 | |
| 			j = i;
 | |
| 			pixel = cc->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)
 | |
| {
 | |
| 	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);
 | |
| 
 | |
| 	*failed = FALSE;
 | |
| 
 | |
| 	for (i = 0; i < cc->num_palette; i++) {
 | |
| 		dr = *red - cc->palette[i].red;
 | |
| 		dg = *green - cc->palette[i].green;
 | |
| 		db = *blue - cc->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;
 | |
| }
 | 
