gtk/gdk/linux-fb/gdkcc-fb.c

1732 lines
39 KiB
C
Raw Normal View History

/* 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 Lesser 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#include <stdlib.h>
#include <string.h>
#include "gdkcc.h"
#include "gdkcolor.h"
#include "gdkprivate-fb.h"
#include "gdkinternals.h"
#define MAX_IMAGE_COLORS 256
typedef struct _GdkColorContextPrivate GdkColorContextPrivate;
struct _GdkColorContextPrivate
{
GdkColorContext color_context;
int red_max, red_mult, green_max, green_mult, blue_max, blue_mult;
int base_pixel;
};
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)
{
int i;
guint32 pixel;
for (i = 0; i < ncolors; i++)
{
pixel = colors[i].pixel;
switch (colormap->visual->depth)
{
case 8:
colors[i] = colormap->colors[pixel];
break;
case 16:
case 24:
case 32:
colors[i].red = (pixel & colormap->visual->red_mask)
<< (16 - colormap->visual->red_prec - colormap->visual->red_shift);
colors[i].green = (pixel & colormap->visual->green_mask)
<< (16 - colormap->visual->green_prec - colormap->visual->green_shift);
colors[i].blue = (pixel & colormap->visual->blue_mask)
<< (16 - colormap->visual->blue_prec - colormap->visual->blue_shift);
break;
default:
g_assert_not_reached ();
break;
}
}
}
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->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 = 256;
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);
ccp->base_pixel = 0;
ccp->red_max = cc->num_colors - 1;
ccp->red_mult = 1;
{
GdkColor colr;
gdk_color_white (cc->colormap, &colr);
cc->white_pixel = colr.pixel;
gdk_color_black (cc->colormap, &colr);
cc->black_pixel = colr.pixel;
}
query_colors (cc);
cc->mode = GDK_CC_MODE_MY_GRAY;
}
static void
init_color (GdkColorContext *cc)
{
GdkColorContextPrivate *ccp = (GdkColorContextPrivate *) cc;
gint cubeval;
GdkColor colr;
cubeval = 1;
while ((cubeval * cubeval * cubeval) < cc->visual->colormap_size)
cubeval++;
cubeval--;
cc->num_colors = cubeval * cubeval * cubeval;
ccp->red_max = cubeval - 1;
ccp->green_max = cubeval - 1;
ccp->blue_max = cubeval - 1;
ccp->red_mult = cubeval * cubeval;
ccp->green_mult = cubeval;
ccp->blue_mult = 1;
ccp->base_pixel = 0;
gdk_color_white (cc->colormap, &colr);
cc->white_pixel = colr.pixel;
gdk_color_black (cc->colormap, &colr);
cc->black_pixel = colr.pixel;
cc->num_colors = cc->visual->colormap_size;
/* 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)
{
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 = 0xFFFFFFFF;
cc->black_pixel = 0;
}
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 (cc->visual->colormap_size == 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)
{
g_hash_table_foreach (cc->color_hash,
free_hash_entry,
NULL);
g_hash_table_destroy (cc->color_hash);
cc->color_hash = NULL;
}
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_new0 (GdkColorContextPrivate, 1);
cc = (GdkColorContext *) ccp;
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? */
&& (colormap == 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 = (colormap != default_colormap);
}
switch (visual->type)
{
case GDK_VISUAL_STATIC_GRAY:
case GDK_VISUAL_GRAYSCALE:
GDK_NOTE (COLOR_CONTEXT,
g_message ("gdk_color_context_new: visual class is %s\n",
(visual->type == GDK_VISUAL_STATIC_GRAY) ?
"GDK_VISUAL_STATIC_GRAY" :
"GDK_VISUAL_GRAYSCALE"));
if (cc->visual->colormap_size == 2)
init_bw (cc);
else
init_gray (cc);
break;
case GDK_VISUAL_TRUE_COLOR: /* shifts */
GDK_NOTE (COLOR_CONTEXT,
g_message ("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_message ("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_message ("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_message ("gdk_color_context_new: screen depth is %i, no. of colors is %i\n",
cc->visual->depth, cc->num_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_new0 (GdkColorContext, 1);
ccp = (GdkColorContextPrivate *) cc;
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);
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->red_max + 1) / 0xffff) > ccp->red_max)
ired = ccp->red_max;
ired *= ccp->red_mult;
if ((igreen = green * (ccp->green_max + 1) / 0xffff) > ccp->green_max)
igreen = ccp->green_max;
igreen *= ccp->green_mult;
if ((iblue = blue * (ccp->blue_max + 1) / 0xffff) > ccp->blue_max)
iblue = ccp->blue_max;
iblue *= ccp->blue_mult;
if (cc->clut != NULL)
return cc->clut[ccp->base_pixel + ired + igreen + iblue];
return ccp->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 = NULL;
color.red = red;
color.green = green;
color.blue = blue;
if (cc->color_hash)
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_message ("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;
if (!cc->color_hash)
cc->color_hash = g_hash_table_new (hash_color, compare_colors);
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_message ("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_message ("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_message ("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_message ("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_message ("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_message ("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_message ("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)
return 0;
/* 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_foreach (cc->color_hash,
free_hash_entry,
NULL);
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;
}