gtk/gdk/win32/gdkcolor-win32.c
Tor Lillqvist ffcd60398a Fixes contributed by Archaeopteryx Software: This is a first small part of
2002-01-12  Tor Lillqvist  <tml@iki.fi>

	Fixes contributed by Archaeopteryx Software: This is a first small
	part of their changes. Will commit more later.

	* gdk/win32/*.c: Add some more checks for failed GDI calls. If a
	call fails, don't use bogus values.

	* gdk/win32/gdkcursor-win32.c (_gdk_win32_data_to_wcursor): Plug
	memory leak: g_free() the AND and XOR bitmaps after use.
	(_gdk_cursor_destroy): If we are destroying the current Windows
	cursor, set the Windows cursor to none first.

	* gdk/win32/gdkgc-win32.c (predraw_set_foreground): Delete the old
	brush that was in the DC, like the win32-procution branch does. I
	guess this plugs a resource leak? With the HDC cache, the old
	brush might be something we created ourselves, and not a stock
	brush. And it doesn't do any harm to call DeleteObject on stock
	brushes.

	* gdk/win32/gdkwindow-win32.c (gdk_window_impl_win32_finalize): If
	the window has a cursor which is the current Windows cursor,
	before destroying it set the current Windows cursor to none.
	(gdk_window_set_cursor): Also here, if destroying the current
	Windows cursor, set the current Windows cursor to none first.
	(gdk_window_get_pointer): Revamp logic.
2002-01-11 23:56:28 +00:00

1431 lines
35 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 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.
*/
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "gdkcolor.h"
#include "gdkinternals.h"
#include "gdkprivate-win32.h"
static void free_colormap (Colormap colormap);
static gint gdk_colormap_match_color (GdkColormap *cmap,
GdkColor *color,
const gchar *available);
static void gdk_colormap_add (GdkColormap *cmap);
static void gdk_colormap_remove (GdkColormap *cmap);
static guint gdk_colormap_hash (Colormap *cmap);
static gboolean gdk_colormap_equal (Colormap *a,
Colormap *b);
static void gdk_colormap_init (GdkColormap *colormap);
static void gdk_colormap_class_init (GdkColormapClass *klass);
static void gdk_colormap_finalize (GObject *object);
static gpointer parent_class = NULL;
static GHashTable *colormap_hash = NULL;
GType
gdk_colormap_get_type (void)
{
static GType object_type = 0;
if (!object_type)
{
static const GTypeInfo object_info =
{
sizeof (GdkColormapClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gdk_colormap_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GdkColormap),
0, /* n_preallocs */
(GInstanceInitFunc) gdk_colormap_init,
};
object_type = g_type_register_static (G_TYPE_OBJECT,
"GdkColormap",
&object_info, 0);
}
return object_type;
}
static void
gdk_colormap_init (GdkColormap *colormap)
{
GdkColormapPrivateWin32 *private;
private = g_new (GdkColormapPrivateWin32, 1);
colormap->windowing_data = private;
private->hash = NULL;
private->last_sync_time = 0;
private->info = NULL;
colormap->size = 0;
colormap->colors = NULL;
}
static void
gdk_colormap_class_init (GdkColormapClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
object_class->finalize = gdk_colormap_finalize;
}
static void
gdk_colormap_finalize (GObject *object)
{
GdkColormap *colormap = GDK_COLORMAP (object);
GdkColormapPrivateWin32 *private = GDK_COLORMAP_PRIVATE_DATA (colormap);
gdk_colormap_remove (colormap);
free_colormap (private->xcolormap);
if (private->hash)
g_hash_table_destroy (private->hash);
g_free (private->info);
g_free (colormap->colors);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean
alloc_color_cells(Colormap colormap,
gboolean contig,
unsigned long plane_masks_return[],
unsigned int nplanes,
unsigned long pixels_return[],
unsigned int npixels)
{
unsigned int i, nfree, iret;
nfree = 0;
for (i = 0; i < colormap->size && nfree < npixels; i++)
if (!colormap->in_use[i])
nfree++;
if (colormap->size + npixels - nfree > colormap->sizepalette)
{
g_warning ("alloc_color_cells: too large palette: %d",
colormap->size + npixels);
return FALSE;
}
iret = 0;
for (i = 0; i < colormap->size && iret < npixels; i++)
if (!colormap->in_use[i])
{
colormap->in_use[i] = TRUE;
pixels_return[iret] = i;
iret++;
}
if (nfree < npixels)
{
int nmore = npixels - nfree;
/* I don't understand why, if the code below in #if 0 is
enabled, gdkrgb fails miserably. The palette doesn't get
realized correctly. There doesn't seem to be any harm done by
keeping this code out, either. */
#ifdef SOME_STRANGE_BUG
if (!ResizePalette (colormap->palette, colormap->size + nmore))
{
WIN32_GDI_FAILED ("ResizePalette")
return FALSE;
}
g_print("alloc_color_cells: %#x to %d\n",
colormap->palette, colormap->size + nmore);
#endif
for (i = colormap->size; i < colormap->size + nmore; i++)
{
pixels_return[iret] = i;
iret++;
colormap->in_use[i] = TRUE;
}
#ifdef SOME_STRANGE_BUG
colormap->size += nmore;
#endif
}
return TRUE;
}
/* The following functions are from Tk8.0, but heavily modified.
Here are tk's licensing terms. I hope these terms don't conflict
with the GNU Lesser General Public License? They shouldn't, as
they are looser that the GLPL, yes? */
/*
This software is copyrighted by the Regents of the University of
California, Sun Microsystems, Inc., and other parties. The following
terms apply to all files associated with the software unless explicitly
disclaimed in individual files.
The authors hereby grant permission to use, copy, modify, distribute,
and license this software and its documentation for any purpose, provided
that existing copyright notices are retained in all copies and that this
notice is included verbatim in any distributions. No written agreement,
license, or royalty fee is required for any of the authorized uses.
Modifications to this software may be copyrighted by their authors
and need not follow the licensing terms described here, provided that
the new terms are clearly indicated on the first page of each file where
they apply.
IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
MODIFICATIONS.
GOVERNMENT USE: If you are acquiring this software on behalf of the
U.S. government, the Government shall have only "Restricted Rights"
in the software and related documentation as defined in the Federal
Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
are acquiring the software on behalf of the Department of Defense, the
software shall be classified as "Commercial Computer Software" and the
Government shall have only "Restricted Rights" as defined in Clause
252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the
authors grant the U.S. Government and others acting in its behalf
permission to use and distribute the software in accordance with the
terms specified in this license.
*/
/*
*----------------------------------------------------------------------
*
* XAllocColor --
*
* Find the closest available color to the specified XColor.
*
* Results:
* Updates the color argument and returns 1 on success. Otherwise
* returns 0.
*
* Side effects:
* Allocates a new color in the palette.
*
*----------------------------------------------------------------------
*/
static int
alloc_color(Colormap colormap,
XColor *color,
guint *pixelp)
{
PALETTEENTRY entry, closeEntry;
unsigned int i;
entry = *color;
entry.peFlags = 0;
if (colormap->rc_palette)
{
COLORREF newPixel, closePixel;
UINT index;
/*
* Find the nearest existing palette entry.
*/
newPixel = RGB (entry.peRed, entry.peGreen, entry.peBlue);
index = GetNearestPaletteIndex (colormap->palette, newPixel);
GetPaletteEntries (colormap->palette, index, 1, &closeEntry);
closePixel = RGB (closeEntry.peRed, closeEntry.peGreen,
closeEntry.peBlue);
if (newPixel != closePixel)
{
/* Not a perfect match. */
if (!colormap->in_use[index])
{
/* It was a free'd entry anyway, so we can use it, and
set it to the correct color. */
if (SetPaletteEntries (colormap->palette, index, 1, &entry) == 0)
WIN32_GDI_FAILED ("SetPaletteEntries");
}
else
{
/* The close entry found is in use, so search for a
unused slot. */
for (i = 0; i < colormap->size; i++)
if (!colormap->in_use[i])
{
/* A free slot, use it. */
if (SetPaletteEntries (colormap->palette,
index, 1, &entry) == 0)
WIN32_GDI_FAILED ("SetPaletteEntries");
index = i;
break;
}
if (i == colormap->size)
{
/* No free slots found. If the palette isn't maximal
yet, grow it. */
if (colormap->size == colormap->sizepalette)
{
/* The palette is maximal, and no free slots available,
so use the close entry, then, dammit. */
*color = closeEntry;
}
else
{
/* There is room to grow the palette. */
index = colormap->size;
colormap->size++;
if (!ResizePalette (colormap->palette, colormap->size))
WIN32_GDI_FAILED ("ResizePalette");
if (SetPaletteEntries (colormap->palette, index, 1, &entry) == 0)
WIN32_GDI_FAILED ("SetPaletteEntries");
}
}
}
colormap->stale = TRUE;
}
else
{
/* We got a match, so use it. */
}
*pixelp = index;
colormap->in_use[index] = TRUE;
#if 0
g_print("alloc_color from %#x: index %d for %02x %02x %02x\n",
colormap->palette, index,
entry.peRed, entry.peGreen, entry.peBlue);
#endif
}
else
{
/*
* Determine what color will actually be used on non-colormap systems.
*/
*pixelp = GetNearestColor (gdk_display_hdc, RGB(entry.peRed, entry.peGreen, entry.peBlue));
color->peRed = GetRValue (*pixelp);
color->peGreen = GetGValue (*pixelp);
color->peBlue = GetBValue (*pixelp);
}
return 1;
}
/*
*----------------------------------------------------------------------
*
* XFreeColors --
*
* Deallocate a block of colors.
*
* Results:
* None.
*
* Side effects:
* Removes entries for the current palette and compacts the
* remaining set.
*
*----------------------------------------------------------------------
*/
static void
free_colors (Colormap colormap,
gulong *pixels,
gint npixels,
gulong planes)
{
gint i;
PALETTEENTRY entries[256];
/*
* We don't have to do anything for non-palette devices.
*/
if (colormap->rc_palette)
{
int npal;
int lowestpixel = 256;
int highestpixel = -1;
npal = GetPaletteEntries (colormap->palette, 0, 256, entries);
for (i = 0; i < npixels; i++)
{
int pixel = pixels[i];
if (pixel < lowestpixel)
lowestpixel = pixel;
if (pixel > highestpixel)
highestpixel = pixel;
colormap->in_use[pixel] = FALSE;
entries[pixel] = entries[0];
}
#if 0
if (SetPaletteEntries (colormap->palette, lowestpixel,
highestpixel - lowestpixel + 1,
entries + lowestpixel) == 0)
WIN32_GDI_FAILED ("SetPaletteEntries");
#endif
colormap->stale = TRUE;
#if 0
g_print("free_colors %#x lowestpixel = %d, highestpixel = %d\n",
colormap->palette, lowestpixel, highestpixel);
#endif
}
}
/*
*----------------------------------------------------------------------
*
* XCreateColormap --
*
* Allocate a new colormap.
*
* Results:
* Returns a newly allocated colormap.
*
* Side effects:
* Allocates an empty palette and color list.
*
*----------------------------------------------------------------------
*/
static Colormap
create_colormap (HWND w,
Visual *visual,
gboolean alloc)
{
char logPalBuf[sizeof(LOGPALETTE) + 256 * sizeof(PALETTEENTRY)];
LOGPALETTE *logPalettePtr;
Colormap colormap;
guint i;
HPALETTE sysPal;
HDC hdc;
/* Should the alloc parameter do something? */
/* Allocate a starting palette with all of the reserved colors. */
logPalettePtr = (LOGPALETTE *) logPalBuf;
logPalettePtr->palVersion = 0x300;
sysPal = (HPALETTE) GetStockObject (DEFAULT_PALETTE);
logPalettePtr->palNumEntries =
GetPaletteEntries (sysPal, 0, 256, logPalettePtr->palPalEntry);
colormap = (Colormap) g_new (ColormapStruct, 1);
colormap->size = logPalettePtr->palNumEntries;
colormap->stale = TRUE;
colormap->palette = CreatePalette (logPalettePtr);
hdc = GetDC (NULL);
colormap->rc_palette = ((GetDeviceCaps (hdc, RASTERCAPS) & RC_PALETTE) != 0);
if (colormap->rc_palette)
{
colormap->sizepalette = GetDeviceCaps (hdc, SIZEPALETTE);
colormap->in_use = g_new (gboolean, colormap->sizepalette);
/* Mark static colors in use. */
for (i = 0; i < logPalettePtr->palNumEntries; i++)
colormap->in_use[i] = TRUE;
/* Mark rest not in use */
for (i = logPalettePtr->palNumEntries; i < colormap->sizepalette; i++)
colormap->in_use[i] = FALSE;
}
if (!ReleaseDC (NULL, hdc))
WIN32_GDI_FAILED ("ReleaseDC");
return colormap;
}
/*
*----------------------------------------------------------------------
*
* XFreeColormap --
*
* Frees the resources associated with the given colormap.
*
* Results:
* None.
*
* Side effects:
* Deletes the palette associated with the colormap. Note that
* the palette must not be selected into a device context when
* this occurs.
*
*----------------------------------------------------------------------
*/
static void
free_colormap(Colormap colormap)
{
if (!DeleteObject (colormap->palette))
{
g_error ("Unable to free colormap, palette is still selected.");
}
g_free (colormap);
}
static Colormap
default_colormap ()
{
static Colormap colormap;
if (colormap)
return colormap;
colormap = create_colormap ( NULL, NULL, FALSE);
return colormap;
}
GdkColormap*
gdk_colormap_new (GdkVisual *visual,
gboolean private_cmap)
{
GdkColormap *colormap;
GdkColormapPrivateWin32 *private;
Visual *xvisual;
int i;
g_return_val_if_fail (visual != NULL, NULL);
colormap = g_object_new (gdk_colormap_get_type (), NULL);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
colormap->visual = visual;
xvisual = ((GdkVisualPrivate*) visual)->xvisual;
colormap->size = visual->colormap_size;
switch (visual->type)
{
case GDK_VISUAL_GRAYSCALE:
case GDK_VISUAL_PSEUDO_COLOR:
private->info = g_new0 (GdkColorInfo, colormap->size);
colormap->colors = g_new (GdkColor, colormap->size);
private->hash = g_hash_table_new ((GHashFunc) gdk_color_hash,
(GEqualFunc) gdk_color_equal);
private->private_val = private_cmap;
private->xcolormap = create_colormap (gdk_root_window, xvisual, private_cmap);
if (private_cmap)
{
PALETTEENTRY pal[256];
guint npal;
npal = GetPaletteEntries (private->xcolormap->palette, 0, colormap->size, pal);
for (i = 0; i < colormap->size; i++)
{
colormap->colors[i].pixel = i;
if (i >= npal)
{
colormap->colors[i].red =
colormap->colors[i].green =
colormap->colors[i].blue = 0;
}
else
{
colormap->colors[i].red = (pal[i].peRed * 65535) / 255;
colormap->colors[i].green = (pal[i].peGreen * 65525) / 255;
colormap->colors[i].blue = (pal[i].peBlue * 65535) / 255;
}
}
gdk_colormap_change (colormap, colormap->size);
}
break;
case GDK_VISUAL_STATIC_GRAY:
case GDK_VISUAL_STATIC_COLOR:
case GDK_VISUAL_TRUE_COLOR:
private->private_val = FALSE;
private->xcolormap = create_colormap (gdk_root_window,
xvisual, FALSE);
break;
case GDK_VISUAL_DIRECT_COLOR:
g_assert_not_reached ();
}
gdk_colormap_add (colormap);
return colormap;
}
#define MIN_SYNC_TIME 2
static void
gdk_colormap_sync (GdkColormap *colormap,
gboolean force)
{
time_t current_time;
GdkColormapPrivateWin32 *private = GDK_COLORMAP_PRIVATE_DATA (colormap);
XColor *xpalette;
gint nlookup;
gint i;
g_return_if_fail (colormap != NULL);
current_time = time (NULL);
if (!force && ((current_time - private->last_sync_time) < MIN_SYNC_TIME))
return;
private->last_sync_time = current_time;
nlookup = 0;
xpalette = g_new (XColor, colormap->size);
nlookup = GetPaletteEntries (private->xcolormap->palette,
0, colormap->size, xpalette);
for (i = 0; i < nlookup; i++)
{
colormap->colors[i].pixel = i;
colormap->colors[i].red = (xpalette[i].peRed * 65535) / 255;
colormap->colors[i].green = (xpalette[i].peGreen * 65535) / 255;
colormap->colors[i].blue = (xpalette[i].peBlue * 65535) / 255;
}
for ( ; i < colormap->size; i++)
{
colormap->colors[i].pixel = i;
colormap->colors[i].red = 0;
colormap->colors[i].green = 0;
colormap->colors[i].blue = 0;
}
g_free (xpalette);
}
GdkColormap*
gdk_colormap_get_system (void)
{
static GdkColormap *colormap = NULL;
GdkColormapPrivateWin32 *private;
if (!colormap)
{
colormap = g_object_new (gdk_colormap_get_type (), NULL);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
private->xcolormap = default_colormap ();
colormap->visual = gdk_visual_get_system ();
private->private_val = FALSE;
private->hash = NULL;
private->last_sync_time = 0;
private->info = NULL;
colormap->colors = NULL;
colormap->size = colormap->visual->colormap_size;
if ((colormap->visual->type == GDK_VISUAL_GRAYSCALE) ||
(colormap->visual->type == GDK_VISUAL_PSEUDO_COLOR))
{
private->info = g_new0 (GdkColorInfo, colormap->size);
colormap->colors = g_new (GdkColor, colormap->size);
private->hash = g_hash_table_new ((GHashFunc) gdk_color_hash,
(GEqualFunc) gdk_color_equal);
gdk_colormap_sync (colormap, TRUE);
}
gdk_colormap_add (colormap);
}
return colormap;
}
gint
gdk_colormap_get_system_size (void)
{
gint bitspixel;
bitspixel = GetDeviceCaps (gdk_display_hdc, BITSPIXEL);
if (bitspixel == 1)
return 2;
else if (bitspixel == 4)
return 16;
else if (bitspixel == 8)
return 256;
else if (bitspixel == 12)
return 32;
else if (bitspixel == 16)
return 64;
else /* if (bitspixel >= 24) */
return 256;
}
void
gdk_colormap_change (GdkColormap *colormap,
gint ncolors)
{
GdkColormapPrivateWin32 *private;
XColor *palette;
int i;
g_return_if_fail (GDK_IS_COLORMAP (colormap));
palette = g_new (XColor, ncolors);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
switch (colormap->visual->type)
{
case GDK_VISUAL_GRAYSCALE:
case GDK_VISUAL_PSEUDO_COLOR:
for (i = 0; i < ncolors; i++)
{
palette[i].peRed = (colormap->colors[i].red >> 8);
palette[i].peGreen = (colormap->colors[i].green >> 8);
palette[i].peBlue = (colormap->colors[i].blue >> 8);
palette[i].peFlags = 0;
}
if (SetPaletteEntries (private->xcolormap->palette,
0, ncolors, palette) == 0)
WIN32_GDI_FAILED ("SetPaletteEntries");
private->xcolormap->stale = TRUE;
break;
default:
break;
}
g_free (palette);
}
gboolean
gdk_colors_alloc (GdkColormap *colormap,
gboolean contiguous,
gulong *planes,
gint nplanes,
gulong *pixels,
gint npixels)
{
GdkColormapPrivateWin32 *private;
gint return_val;
gint i;
g_return_val_if_fail (GDK_IS_COLORMAP (colormap), 0);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
return_val = alloc_color_cells (private->xcolormap, contiguous,
planes, nplanes, pixels, npixels);
if (return_val)
{
for (i=0; i<npixels; i++)
{
private->info[pixels[i]].ref_count++;
private->info[pixels[i]].flags |= GDK_COLOR_WRITEABLE;
}
}
return return_val != 0;
}
/* This is almost identical to gdk_colormap_free_colors.
* Keep them in sync!
*/
void
gdk_colors_free (GdkColormap *colormap,
gulong *in_pixels,
gint in_npixels,
gulong planes)
{
GdkColormapPrivateWin32 *private;
gulong *pixels;
gint npixels = 0;
gint i;
g_return_if_fail (GDK_IS_COLORMAP (colormap));
g_return_if_fail (in_pixels != NULL);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
if ((colormap->visual->type != GDK_VISUAL_PSEUDO_COLOR) &&
(colormap->visual->type != GDK_VISUAL_GRAYSCALE))
return;
pixels = g_new (gulong, in_npixels);
for (i=0; i<in_npixels; i++)
{
gulong pixel = in_pixels[i];
if (private->info[pixel].ref_count)
{
private->info[pixel].ref_count--;
if (private->info[pixel].ref_count == 0)
{
pixels[npixels++] = pixel;
if (!(private->info[pixel].flags & GDK_COLOR_WRITEABLE))
g_hash_table_remove (private->hash, &colormap->colors[pixel]);
private->info[pixel].flags = 0;
}
}
}
if (npixels)
free_colors (private->xcolormap, pixels, npixels, planes);
g_free (pixels);
}
/* This is almost identical to gdk_colors_free.
* Keep them in sync!
*/
void
gdk_colormap_free_colors (GdkColormap *colormap,
GdkColor *colors,
gint ncolors)
{
GdkColormapPrivateWin32 *private;
gulong *pixels;
gint npixels = 0;
gint i;
g_return_if_fail (GDK_IS_COLORMAP (colormap));
g_return_if_fail (colors != NULL);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
if ((colormap->visual->type != GDK_VISUAL_PSEUDO_COLOR) &&
(colormap->visual->type != GDK_VISUAL_GRAYSCALE))
return;
pixels = g_new (gulong, ncolors);
for (i=0; i<ncolors; i++)
{
gulong pixel = colors[i].pixel;
if (private->info[pixel].ref_count)
{
private->info[pixel].ref_count--;
if (private->info[pixel].ref_count == 0)
{
pixels[npixels++] = pixel;
if (!(private->info[pixel].flags & GDK_COLOR_WRITEABLE))
g_hash_table_remove (private->hash, &colormap->colors[pixel]);
private->info[pixel].flags = 0;
}
}
}
if (npixels)
free_colors (private->xcolormap, pixels, npixels, 0);
g_free (pixels);
}
/********************
* Color allocation *
********************/
/* Try to allocate a single color using alloc_color. If it succeeds,
* cache the result in our colormap, and store in ret.
*/
static gboolean
gdk_colormap_alloc1 (GdkColormap *colormap,
GdkColor *color,
GdkColor *ret)
{
GdkColormapPrivateWin32 *private;
XColor xcolor;
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
xcolor.peRed = color->red >> 8;
xcolor.peGreen = color->green >> 8;
xcolor.peBlue = color->blue >> 8;
if (alloc_color (private->xcolormap, &xcolor, &ret->pixel))
{
ret->red = (xcolor.peRed * 65535) / 255;
ret->green = (xcolor.peGreen * 65535) / 255;
ret->blue = (xcolor.peBlue * 65535) / 255;
if ((guint) ret->pixel < colormap->size)
{
if (private->info[ret->pixel].ref_count) /* got a duplicate */
{
/* XXX */
}
else
{
colormap->colors[ret->pixel] = *color;
private->info[ret->pixel].ref_count = 1;
g_hash_table_insert (private->hash,
&colormap->colors[ret->pixel],
&colormap->colors[ret->pixel]);
}
}
return TRUE;
}
else
{
return FALSE;
}
}
static gint
gdk_colormap_alloc_colors_writeable (GdkColormap *colormap,
GdkColor *colors,
gint ncolors,
gboolean writeable,
gboolean best_match,
gboolean *success)
{
GdkColormapPrivateWin32 *private;
gulong *pixels;
gboolean status;
gint i, index;
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
if (private->private_val)
{
index = 0;
for (i=0; i<ncolors; i++)
{
while ((index < colormap->size) && (private->info[index].ref_count != 0))
index++;
if (index < colormap->size)
{
colors[i].pixel = index;
success[i] = TRUE;
private->info[index].ref_count++;
private->info[i].flags |= GDK_COLOR_WRITEABLE;
}
else
break;
}
return i;
}
else
{
pixels = g_new (gulong, ncolors);
/* Allocation of a writeable color cells */
status = alloc_color_cells (private->xcolormap, FALSE, NULL,
0, pixels, ncolors);
if (status)
{
for (i=0; i<ncolors; i++)
{
colors[i].pixel = pixels[i];
private->info[pixels[i]].ref_count++;
private->info[pixels[i]].flags |= GDK_COLOR_WRITEABLE;
}
}
g_free (pixels);
return status ? ncolors : 0;
}
}
static gint
gdk_colormap_alloc_colors_private (GdkColormap *colormap,
GdkColor *colors,
gint ncolors,
gboolean writeable,
gboolean best_match,
gboolean *success)
{
GdkColormapPrivateWin32 *private;
gint i, index;
XColor *store = g_new (XColor, ncolors);
gint nstore = 0;
gint nremaining = 0;
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
index = -1;
/* First, store the colors we have room for */
index = 0;
for (i=0; i<ncolors; i++)
{
if (!success[i])
{
while ((index < colormap->size) && (private->info[index].ref_count != 0))
index++;
if (index < colormap->size)
{
store[nstore].peRed = colors[i].red >> 8;
store[nstore].peBlue = colors[i].blue >> 8;
store[nstore].peGreen = colors[i].green >> 8;
nstore++;
success[i] = TRUE;
colors[i].pixel = index;
private->info[index].ref_count++;
}
else
nremaining++;
}
}
if (SetPaletteEntries (private->xcolormap->palette,
0, nstore, store) == 0)
WIN32_GDI_FAILED ("SetPaletteEntries");
private->xcolormap->stale = TRUE;
g_free (store);
if (nremaining > 0 && best_match)
{
/* Get best matches for remaining colors */
gchar *available = g_new (gchar, colormap->size);
for (i = 0; i < colormap->size; i++)
available[i] = TRUE;
for (i=0; i<ncolors; i++)
{
if (!success[i])
{
index = gdk_colormap_match_color (colormap,
&colors[i],
available);
if (index != -1)
{
colors[i] = colormap->colors[index];
private->info[index].ref_count++;
success[i] = TRUE;
nremaining--;
}
}
}
g_free (available);
}
return (ncolors - nremaining);
}
static gint
gdk_colormap_alloc_colors_shared (GdkColormap *colormap,
GdkColor *colors,
gint ncolors,
gboolean writeable,
gboolean best_match,
gboolean *success)
{
GdkColormapPrivateWin32 *private;
gint i, index;
gint nremaining = 0;
gint nfailed = 0;
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
index = -1;
for (i=0; i<ncolors; i++)
{
if (!success[i])
{
if (gdk_colormap_alloc1 (colormap, &colors[i], &colors[i]))
success[i] = TRUE;
else
nremaining++;
}
}
if (nremaining > 0 && best_match)
{
gchar *available = g_new (gchar, colormap->size);
for (i = 0; i < colormap->size; i++)
available[i] = ((private->info[i].ref_count == 0) ||
!(private->info[i].flags & GDK_COLOR_WRITEABLE));
gdk_colormap_sync (colormap, FALSE);
while (nremaining > 0)
{
for (i=0; i<ncolors; i++)
{
if (!success[i])
{
index = gdk_colormap_match_color (colormap, &colors[i], available);
if (index != -1)
{
if (private->info[index].ref_count)
{
private->info[index].ref_count++;
colors[i] = colormap->colors[index];
success[i] = TRUE;
nremaining--;
}
else
{
if (gdk_colormap_alloc1 (colormap,
&colormap->colors[index],
&colors[i]))
{
success[i] = TRUE;
nremaining--;
break;
}
else
{
available[index] = FALSE;
}
}
}
else
{
nfailed++;
nremaining--;
success[i] = 2; /* flag as permanent failure */
}
}
}
}
g_free (available);
}
/* Change back the values we flagged as permanent failures */
if (nfailed > 0)
{
for (i=0; i<ncolors; i++)
if (success[i] == 2)
success[i] = FALSE;
nremaining = nfailed;
}
return (ncolors - nremaining);
}
static gint
gdk_colormap_alloc_colors_pseudocolor (GdkColormap *colormap,
GdkColor *colors,
gint ncolors,
gboolean writeable,
gboolean best_match,
gboolean *success)
{
GdkColormapPrivateWin32 *private;
GdkColor *lookup_color;
gint i;
gint nremaining = 0;
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
/* Check for an exact match among previously allocated colors */
for (i=0; i<ncolors; i++)
{
if (!success[i])
{
lookup_color = g_hash_table_lookup (private->hash, &colors[i]);
if (lookup_color)
{
private->info[lookup_color->pixel].ref_count++;
colors[i].pixel = lookup_color->pixel;
success[i] = TRUE;
}
else
nremaining++;
}
}
/* If that failed, we try to allocate a new color, or approxmiate
* with what we can get if best_match is TRUE.
*/
if (nremaining > 0)
{
if (private->private_val)
return gdk_colormap_alloc_colors_private (colormap, colors, ncolors, writeable, best_match, success);
else
return gdk_colormap_alloc_colors_shared (colormap, colors, ncolors, writeable, best_match, success);
}
else
return 0;
}
gint
gdk_colormap_alloc_colors (GdkColormap *colormap,
GdkColor *colors,
gint ncolors,
gboolean writeable,
gboolean best_match,
gboolean *success)
{
GdkColormapPrivateWin32 *private;
GdkVisual *visual;
gint i;
gint nremaining = 0;
XColor xcolor;
g_return_val_if_fail (GDK_IS_COLORMAP (colormap), FALSE);
g_return_val_if_fail (colors != NULL, FALSE);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
for (i=0; i<ncolors; i++)
{
success[i] = FALSE;
}
switch (colormap->visual->type)
{
case GDK_VISUAL_PSEUDO_COLOR:
case GDK_VISUAL_GRAYSCALE:
if (writeable)
return gdk_colormap_alloc_colors_writeable (colormap, colors, ncolors,
writeable, best_match, success);
else
return gdk_colormap_alloc_colors_pseudocolor (colormap, colors, ncolors,
writeable, best_match, success);
break;
case GDK_VISUAL_TRUE_COLOR:
visual = colormap->visual;
for (i=0; i<ncolors; i++)
{
colors[i].pixel = (((colors[i].red >> (16 - visual->red_prec)) << visual->red_shift) +
((colors[i].green >> (16 - visual->green_prec)) << visual->green_shift) +
((colors[i].blue >> (16 - visual->blue_prec)) << visual->blue_shift));
success[i] = TRUE;
}
break;
case GDK_VISUAL_STATIC_GRAY:
case GDK_VISUAL_STATIC_COLOR:
for (i=0; i<ncolors; i++)
{
xcolor.peRed = colors[i].red >> 8;
xcolor.peGreen = colors[i].green >> 8;
xcolor.peBlue = colors[i].blue >> 8;
if (alloc_color (private->xcolormap, &xcolor, &colors[i].pixel))
success[i] = TRUE;
else
nremaining++;
}
break;
case GDK_VISUAL_DIRECT_COLOR:
g_assert_not_reached ();
}
return nremaining;
}
void
gdk_colormap_query_color (GdkColormap *colormap,
gulong pixel,
GdkColor *result)
{
GdkVisual *visual;
g_return_if_fail (GDK_IS_COLORMAP (colormap));
visual = gdk_colormap_get_visual (colormap);
switch (visual->type) {
case GDK_VISUAL_DIRECT_COLOR:
case GDK_VISUAL_TRUE_COLOR:
result->red = 65535. * (double)((pixel & visual->red_mask) >> visual->red_shift) / ((1 << visual->red_prec) - 1);
result->green = 65535. * (double)((pixel & visual->green_mask) >> visual->green_shift) / ((1 << visual->green_prec) - 1);
result->blue = 65535. * (double)((pixel & visual->blue_mask) >> visual->blue_shift) / ((1 << visual->blue_prec) - 1);
break;
case GDK_VISUAL_STATIC_GRAY:
case GDK_VISUAL_GRAYSCALE:
result->red = result->green = result->blue = 65535. * (double)pixel/((1<<visual->depth) - 1);
break;
case GDK_VISUAL_STATIC_COLOR:
g_assert_not_reached ();
break;
case GDK_VISUAL_PSEUDO_COLOR:
result->red = colormap->colors[pixel].red;
result->green = colormap->colors[pixel].green;
result->blue = colormap->colors[pixel].blue;
break;
default:
g_assert_not_reached ();
break;
}
}
gboolean
gdk_color_change (GdkColormap *colormap,
GdkColor *color)
{
GdkColormapPrivateWin32 *private;
XColor xcolor;
g_return_val_if_fail (GDK_IS_COLORMAP (colormap), FALSE);
g_return_val_if_fail (color != NULL, FALSE);
private = GDK_COLORMAP_PRIVATE_DATA (colormap);
xcolor.peRed = color->red >> 8;
xcolor.peGreen = color->green >> 8;
xcolor.peBlue = color->blue >> 8;
if (SetPaletteEntries (private->xcolormap->palette,
color->pixel, 1, &xcolor) == 0)
WIN32_GDI_FAILED ("SetPaletteEntries");
private->xcolormap->stale = TRUE;
return TRUE;
}
static gint
gdk_colormap_match_color (GdkColormap *cmap,
GdkColor *color,
const gchar *available)
{
GdkColor *colors;
guint sum, max;
gint rdiff, gdiff, bdiff;
gint i, index;
g_return_val_if_fail (cmap != NULL, 0);
g_return_val_if_fail (color != NULL, 0);
colors = cmap->colors;
max = 3 * (65536);
index = -1;
for (i = 0; i < cmap->size; i++)
{
if ((!available) || (available && available[i]))
{
rdiff = (color->red - colors[i].red);
gdiff = (color->green - colors[i].green);
bdiff = (color->blue - colors[i].blue);
sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
if (sum < max)
{
index = i;
max = sum;
}
}
}
return index;
}
GdkColormap*
gdk_colormap_lookup (Colormap xcolormap)
{
GdkColormap *cmap;
if (!colormap_hash)
return NULL;
cmap = g_hash_table_lookup (colormap_hash, &xcolormap);
return cmap;
}
static void
gdk_colormap_add (GdkColormap *cmap)
{
GdkColormapPrivateWin32 *private;
if (!colormap_hash)
colormap_hash = g_hash_table_new ((GHashFunc) gdk_colormap_hash,
(GEqualFunc) gdk_colormap_equal);
private = GDK_COLORMAP_PRIVATE_DATA (cmap);
g_hash_table_insert (colormap_hash, &private->xcolormap, cmap);
}
static void
gdk_colormap_remove (GdkColormap *cmap)
{
GdkColormapPrivateWin32 *private;
if (!colormap_hash)
colormap_hash = g_hash_table_new ((GHashFunc) gdk_colormap_hash,
(GEqualFunc) gdk_colormap_equal);
private = GDK_COLORMAP_PRIVATE_DATA (cmap);
g_hash_table_remove (colormap_hash, &private->xcolormap);
}
static guint
gdk_colormap_hash (Colormap *cmap)
{
return (guint) *cmap;
}
static gboolean
gdk_colormap_equal (Colormap *a,
Colormap *b)
{
return (*a == *b);
}
#ifdef G_ENABLE_DEBUG
gchar *
gdk_win32_color_to_string (const GdkColor *color)
{
static char buf[100];
sprintf (buf, "(%.04x,%.04x,%.04x):%.06x",
color->red, color->green, color->blue, color->pixel);
return buf;
}
#endif