mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-25 05:01:09 +00:00
555aa2baf9
-- sg
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;
|
|
}
|