mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-10 10:50:10 +00:00
1726 lines
38 KiB
C
1726 lines
38 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., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, 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
|
|
*/
|
|
|
|
|
|
#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 (gconstpointer key)
|
|
{
|
|
const GdkColor *color = key;
|
|
|
|
return (color->red * 33023 + color->green * 30013 + color->blue * 27011);
|
|
}
|
|
|
|
static gint
|
|
compare_colors (gconstpointer a,
|
|
gconstpointer b)
|
|
{
|
|
const GdkColor *aa = a;
|
|
const 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);
|
|
|
|
ccp = g_new (GdkColorContextPrivate, 1);
|
|
cc = (GdkColorContext *) ccp;
|
|
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:
|
|
GDK_NOTE (COLOR_CONTEXT,
|
|
g_print ("gdk_color_context_new: visual class is %s\n",
|
|
(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 */
|
|
GDK_NOTE (COLOR_CONTEXT,
|
|
g_print ("gdk_color_context_new: visual class is GDK_VISUAL_TRUE_COLOR\n"));
|
|
|
|
init_true_color (cc);
|
|
break;
|
|
|
|
case GDK_VISUAL_DIRECT_COLOR: /* shifts and fake CLUT */
|
|
GDK_NOTE (COLOR_CONTEXT,
|
|
g_print ("gdk_color_context_new: visual class is GDK_VISUAL_DIRECT_COLOR\n"));
|
|
|
|
init_direct_color (cc);
|
|
break;
|
|
|
|
case GDK_VISUAL_STATIC_COLOR:
|
|
case GDK_VISUAL_PSEUDO_COLOR:
|
|
GDK_NOTE (COLOR_CONTEXT,
|
|
g_print ("gdk_color_context_new: visual class is %s\n",
|
|
(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;
|
|
|
|
GDK_NOTE (COLOR_CONTEXT,
|
|
g_print ("gdk_color_context_new: screen depth is %i, no. of colors is %i\n",
|
|
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;
|
|
|
|
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 = 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;
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
GDK_NOTE (COLOR_CONTEXT,
|
|
g_print ("gdk_color_context_get_pixel: "
|
|
"resizing CLUT to %i entries\n",
|
|
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];
|
|
#ifdef G_ENABLE_DEBUG
|
|
gint exact_col = 0, subst_col = 0, close_col = 0, black_col = 0;
|
|
#endif
|
|
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))
|
|
{
|
|
GDK_NOTE (COLOR_CONTEXT,
|
|
g_print ("gdk_color_context_get_pixels: got all %i colors; "
|
|
"(%i colors allocated so far)\n", 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 G_ENABLE_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);
|
|
|
|
/* 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 = 0x1000000;
|
|
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++)
|
|
{
|
|
/* Don't replace these by shifts; the sign may get clobbered */
|
|
|
|
rd = (ri - cmap[j].red) / 256;
|
|
gd = (gi - cmap[j].green) / 256;
|
|
bd = (bi - cmap[j].blue) / 256;
|
|
|
|
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 G_ENABLE_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))
|
|
{
|
|
GDK_NOTE (COLOR_CONTEXT,
|
|
g_print ("gdk_color_context_get_pixels: got %i colors, %i exact and "
|
|
"%i close (%i colors allocated so far)\n",
|
|
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 = 0x1000000;
|
|
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];
|
|
|
|
/* Don't replace these by shifts; the sign may get clobbered */
|
|
|
|
rd = (ri - defs[k].red) / 256;
|
|
gd = (gi - defs[k].green) / 256;
|
|
bd = (bi - defs[k].blue) / 256;
|
|
|
|
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 G_ENABLE_DEBUG
|
|
black_col++;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
defs[i] = defs[close];
|
|
#ifdef G_ENABLE_DEBUG
|
|
subst_col++;
|
|
#endif
|
|
}
|
|
|
|
colors[i] = defs[i].pixel;
|
|
}
|
|
while (++idx < nopen);
|
|
|
|
GDK_NOTE (COLOR_CONTEXT,
|
|
g_print ("gdk_color_context_get_pixels: got %i colors, %i exact, %i close, "
|
|
"%i substituted, %i to black (%i colors allocated so far)\n",
|
|
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];
|
|
#ifdef G_ENABLE_DEBUG
|
|
gint exact_col = 0, subst_col = 0, close_col = 0, black_col = 0;
|
|
#endif
|
|
|
|
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
|
|
GDK_NOTE (COLOR_CONTEXT,
|
|
g_print ("gdk_color_context_get_pixels_incremental: "
|
|
"pixel at slot %i already allocated, skipping\n", i));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
*nallocated = ncols;
|
|
|
|
if ((ncols == ncolors) || (nopen == 0))
|
|
{
|
|
GDK_NOTE (COLOR_CONTEXT,
|
|
g_print ("gdk_color_context_get_pixels_incremental: got all %i colors "
|
|
"(%i colors allocated so far)\n",
|
|
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 G_ENABLE_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 */
|
|
|
|
my_x_query_colors (cc->colormap, cmap, cmapsize);
|
|
|
|
/* 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 = 0x1000000;
|
|
close = -1;
|
|
|
|
/* store */
|
|
|
|
ri = reds[i];
|
|
gi = greens[i];
|
|
bi = blues[i];
|
|
|
|
for (j = 0; (j < cmapsize) && (mdist != 0); j++)
|
|
{
|
|
/* Don't replace these by shifts; the sign may get clobbered */
|
|
|
|
rd = (ri - cmap[j].red) / 256;
|
|
gd = (gi - cmap[j].green) / 256;
|
|
bd = (bi - cmap[j].blue) / 256;
|
|
|
|
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 G_ENABLE_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))
|
|
{
|
|
GDK_NOTE (COLOR_CONTEXT,
|
|
g_print ("gdk_color_context_get_pixels_incremental: "
|
|
"got %i colors, %i exact and %i close "
|
|
"(%i colors allocated so far)\n",
|
|
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 = 0x1000000;
|
|
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 */
|
|
/* Don't replace these by shifts; the sign may get clobbered */
|
|
|
|
rd = (ri - defs[k].red) / 256;
|
|
gd = (gi - defs[k].green) / 256;
|
|
bd = (bi - defs[k].blue) / 256;
|
|
|
|
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 G_ENABLE_DEBUG
|
|
black_col++;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
defs[i] = defs[close];
|
|
#ifdef G_ENABLE_DEBUG
|
|
subst_col++;
|
|
#endif
|
|
}
|
|
|
|
colors[i] = defs[i].pixel;
|
|
}
|
|
while (++idx < nopen);
|
|
|
|
GDK_NOTE (COLOR_CONTEXT,
|
|
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)\n",
|
|
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) >> cc->shifts.red) << (16 - cc->bits.red);
|
|
tc->green = ((tc->pixel & cc->masks.green) >> cc->shifts.green) << (16 - cc->bits.green);
|
|
tc->blue = ((tc->pixel & cc->masks.blue) >> cc->shifts.blue) << (16 - cc->bits.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;
|
|
}
|