gtk2/gtk/gtkpreview.c

1643 lines
39 KiB
C
Raw Normal View History

1997-11-24 22:37:52 +00:00
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
1997-11-24 22:37:52 +00:00
*/
#include <math.h>
#include <string.h>
#include <sys/types.h>
#include <sys/param.h>
#include "gdk/gdkx.h"
#include "gtkpreview.h"
#include "gtksignal.h"
#define IMAGE_SIZE 256
#define PREVIEW_CLASS(w) GTK_PREVIEW_CLASS (GTK_OBJECT (w)->klass)
#define COLOR_COMPOSE(r,g,b) (lookup_red[r] | lookup_green[g] | lookup_blue[b])
typedef struct _GtkPreviewProp GtkPreviewProp;
typedef void (*GtkTransferFunc) (guchar *dest, guchar *src, gint count);
struct _GtkPreviewProp
{
guint16 ref_count;
guint16 nred_shades;
guint16 ngreen_shades;
guint16 nblue_shades;
guint16 ngray_shades;
};
static void gtk_preview_class_init (GtkPreviewClass *klass);
static void gtk_preview_init (GtkPreview *preview);
static void gtk_preview_finalize (GtkObject *object);
1997-11-24 22:37:52 +00:00
static void gtk_preview_realize (GtkWidget *widget);
static gint gtk_preview_expose (GtkWidget *widget,
GdkEventExpose *event);
static void gtk_preview_make_buffer (GtkPreview *preview);
static void gtk_preview_get_visuals (GtkPreviewClass *klass);
static void gtk_preview_get_cmaps (GtkPreviewClass *klass);
static void gtk_preview_dither_init (GtkPreviewClass *klass);
static void gtk_fill_lookup_array (gulong *array,
int depth,
int shift,
int prec);
static void gtk_trim_cmap (GtkPreviewClass *klass);
static void gtk_create_8_bit (GtkPreviewClass *klass);
static void gtk_color_8 (guchar *src,
guchar *data,
gint x,
gint y,
gulong width);
static void gtk_color_16 (guchar *src,
guchar *data,
gulong width);
static void gtk_color_24 (guchar *src,
guchar *data,
gulong width);
static void gtk_grayscale_8 (guchar *src,
guchar *data,
gint x,
gint y,
gulong width);
static void gtk_grayscale_16 (guchar *src,
guchar *data,
gulong width);
static void gtk_grayscale_24 (guchar *src,
guchar *data,
gulong width);
static gint gtk_get_preview_prop (guint *nred,
guint *nblue,
guint *ngreen,
guint *ngray);
static void gtk_set_preview_prop (guint nred,
guint ngreen,
guint nblue,
guint ngray);
/* transfer functions:
* destination byte order/source bpp/destination bpp
*/
static void gtk_lsbmsb_1_1 (guchar *dest,
guchar *src,
gint count);
static void gtk_lsb_2_2 (guchar *dest,
guchar *src,
gint count);
static void gtk_msb_2_2 (guchar *dest,
guchar *src,
gint count);
static void gtk_lsb_3_3 (guchar *dest,
guchar *src,
gint count);
static void gtk_msb_3_3 (guchar *dest,
guchar *src,
gint count);
static void gtk_lsb_3_4 (guchar *dest,
guchar *src,
gint count);
static void gtk_msb_3_4 (guchar *dest,
guchar *src,
gint count);
static GtkWidgetClass *parent_class = NULL;
static GtkPreviewClass *preview_class = NULL;
static GtkPreviewInfo *preview_info = NULL;
static gint install_cmap = FALSE;
guint
gtk_preview_get_type ()
{
static guint preview_type = 0;
if (!preview_type)
{
GtkTypeInfo preview_info =
{
"GtkPreview",
sizeof (GtkPreview),
sizeof (GtkPreviewClass),
(GtkClassInitFunc) gtk_preview_class_init,
(GtkObjectInitFunc) gtk_preview_init,
(GtkArgSetFunc) NULL,
(GtkArgGetFunc) NULL,
1997-11-24 22:37:52 +00:00
};
preview_type = gtk_type_unique (gtk_widget_get_type (), &preview_info);
}
return preview_type;
}
static void
gtk_preview_class_init (GtkPreviewClass *klass)
{
GtkObjectClass *object_class;
GtkWidgetClass *widget_class;
object_class = (GtkObjectClass*) klass;
widget_class = (GtkWidgetClass*) klass;
parent_class = gtk_type_class (gtk_widget_get_type ());
preview_class = klass;
object_class->finalize = gtk_preview_finalize;
1997-11-24 22:37:52 +00:00
widget_class->realize = gtk_preview_realize;
widget_class->expose_event = gtk_preview_expose;
if (preview_info)
klass->info = *preview_info;
else
{
klass->info.visual = NULL;
klass->info.cmap = NULL;
klass->info.color_pixels = NULL;
klass->info.gray_pixels = NULL;
klass->info.reserved_pixels = NULL;
klass->info.lookup_red = NULL;
klass->info.lookup_green = NULL;
klass->info.lookup_blue = NULL;
klass->info.dither_red = NULL;
klass->info.dither_green = NULL;
klass->info.dither_blue = NULL;
klass->info.dither_gray = NULL;
klass->info.dither_matrix = NULL;
klass->info.nred_shades = 6;
klass->info.ngreen_shades = 6;
klass->info.nblue_shades = 4;
klass->info.ngray_shades = 24;
klass->info.nreserved = 0;
klass->info.bpp = 0;
klass->info.cmap_alloced = FALSE;
klass->info.gamma = 1.0;
}
klass->image = NULL;
gtk_preview_get_visuals (klass);
gtk_preview_get_cmaps (klass);
gtk_preview_dither_init (klass);
}
void
gtk_preview_reset (void)
{
GtkPreviewInfo *info;
if (!preview_class || !preview_info)
return;
info = &preview_class->info;
gtk_preview_uninit();
if (info->color_pixels)
{
gdk_colors_free (info->cmap,
info->color_pixels,
info->nred_shades *
info->ngreen_shades *
info->nblue_shades,
0);
gdk_colors_free (info->cmap,
info->gray_pixels,
info->ngray_shades, 0);
g_free (info->color_pixels);
g_free (info->gray_pixels);
}
if (info->reserved_pixels)
{
gdk_colors_free (info->cmap,
info->reserved_pixels,
info->nreserved, 0);
g_free (info->reserved_pixels);
}
if (info->cmap && info->cmap_alloced)
gdk_colormap_unref (info->cmap);
if (info->lookup_red)
{
g_free (info->lookup_red);
g_free (info->lookup_green);
g_free (info->lookup_blue);
}
if (info->dither_matrix)
{
int i, j;
for (i= 0 ; i < 8 ; i++)
{
for (j = 0; j < 8 ; j++)
g_free (info->dither_matrix[i][j]);
g_free (info->dither_matrix[i]);
}
g_free (info->dither_matrix);
g_free (info->dither_red);
g_free (info->dither_green);
g_free (info->dither_blue);
}
preview_class->info = *preview_info;
gtk_preview_get_visuals (preview_class);
gtk_preview_get_cmaps (preview_class);
gtk_preview_dither_init (preview_class);
}
1997-11-24 22:37:52 +00:00
static void
gtk_preview_init (GtkPreview *preview)
{
GTK_WIDGET_SET_FLAGS (preview, GTK_BASIC);
preview->buffer = NULL;
preview->buffer_width = 0;
preview->buffer_height = 0;
preview->expand = FALSE;
}
void
gtk_preview_uninit ()
{
GtkPreviewProp *prop;
GdkAtom property;
/* FIXME: need to grab the server here to prevent a race condition */
if (preview_class && !install_cmap && preview_class->info.visual &&
1997-11-24 22:37:52 +00:00
(preview_class->info.visual->type != GDK_VISUAL_TRUE_COLOR) &&
(preview_class->info.visual->type != GDK_VISUAL_DIRECT_COLOR))
{
property = gdk_atom_intern ("GTK_PREVIEW_INFO", FALSE);
if (gdk_property_get (NULL, property, property,
0, sizeof (GtkPreviewProp), FALSE,
NULL, NULL, NULL, (guchar**) &prop))
{
prop->ref_count = prop->ref_count - 1;
1997-11-24 22:37:52 +00:00
if (prop->ref_count == 0)
{
gdk_property_delete (NULL, property);
}
else
{
gdk_property_change (NULL, property, property, 16,
GDK_PROP_MODE_REPLACE,
(guchar*) prop, 5);
}
}
}
}
GtkWidget*
gtk_preview_new (GtkPreviewType type)
{
GtkPreview *preview;
preview = gtk_type_new (gtk_preview_get_type ());
preview->type = type;
return GTK_WIDGET (preview);
}
void
gtk_preview_size (GtkPreview *preview,
gint width,
gint height)
{
g_return_if_fail (preview != NULL);
g_return_if_fail (GTK_IS_PREVIEW (preview));
if ((width != GTK_WIDGET (preview)->requisition.width) ||
(height != GTK_WIDGET (preview)->requisition.height))
{
GTK_WIDGET (preview)->requisition.width = width;
GTK_WIDGET (preview)->requisition.height = height;
if (preview->buffer)
g_free (preview->buffer);
preview->buffer = NULL;
}
}
void
gtk_preview_put (GtkPreview *preview,
GdkWindow *window,
GdkGC *gc,
gint srcx,
gint srcy,
gint destx,
gint desty,
gint width,
gint height)
{
GtkWidget *widget;
GdkImage *image;
GdkRectangle r1, r2, r3;
GtkTransferFunc transfer_func;
guchar *image_mem;
guchar *src, *dest;
gint x, xe, x2;
gint y, ye, y2;
guint dest_rowstride;
guint src_bpp;
guint dest_bpp;
gint i;
g_return_if_fail (preview != NULL);
g_return_if_fail (GTK_IS_PREVIEW (preview));
g_return_if_fail (window != NULL);
if (!preview->buffer)
return;
widget = GTK_WIDGET (preview);
r1.x = 0;
r1.y = 0;
1997-11-24 22:37:52 +00:00
r1.width = preview->buffer_width;
r1.height = preview->buffer_height;
r2.x = srcx;
r2.y = srcy;
1997-11-24 22:37:52 +00:00
r2.width = width;
r2.height = height;
if (!gdk_rectangle_intersect (&r1, &r2, &r3))
return;
x2 = r3.x + r3.width;
y2 = r3.y + r3.height;
if (!preview_class->image)
preview_class->image = gdk_image_new (GDK_IMAGE_FASTEST,
preview_class->info.visual,
IMAGE_SIZE, IMAGE_SIZE);
image = preview_class->image;
src_bpp = preview_class->info.bpp;
image_mem = image->mem;
dest_bpp = image->bpp;
dest_rowstride = image->bpl;
transfer_func = NULL;
switch (dest_bpp)
{
case 1:
switch (src_bpp)
{
case 1:
transfer_func = gtk_lsbmsb_1_1;
break;
}
break;
case 2:
switch (src_bpp)
{
case 2:
if (image->byte_order == GDK_MSB_FIRST)
transfer_func = gtk_msb_2_2;
else
transfer_func = gtk_lsb_2_2;
break;
case 3:
break;
}
break;
case 3:
switch (src_bpp)
{
case 3:
if (image->byte_order == GDK_MSB_FIRST)
transfer_func = gtk_msb_3_3;
else
transfer_func = gtk_lsb_3_3;
break;
}
break;
case 4:
switch (src_bpp)
{
case 3:
if (image->byte_order == GDK_MSB_FIRST)
transfer_func = gtk_msb_3_4;
else
transfer_func = gtk_lsb_3_4;
break;
}
break;
}
if (!transfer_func)
{
g_warning ("unsupported byte order/src bpp/dest bpp combination: %s:%d:%d",
(image->byte_order == GDK_MSB_FIRST) ? "msb" : "lsb", src_bpp, dest_bpp);
return;
}
for (y = r3.y; y < y2; y += IMAGE_SIZE)
{
for (x = r3.x; x < x2; x += IMAGE_SIZE)
{
xe = x + IMAGE_SIZE;
if (xe > x2)
xe = x2;
ye = y + IMAGE_SIZE;
if (ye > y2)
ye = y2;
for (i = y; i < ye; i++)
{
src = preview->buffer + (((gulong) (i - r1.y) * (gulong) preview->buffer_width) +
(x - r1.x)) * (gulong) src_bpp;
dest = image_mem + ((gulong) (i - y) * dest_rowstride);
if (xe > x)
(* transfer_func) (dest, src, xe - x);
}
gdk_draw_image (window, gc,
image, 0, 0,
destx + (r3.x - srcx) + (x - r3.x),
desty + (r3.y - srcy) + (y - r3.y),
1997-11-24 22:37:52 +00:00
xe - x, ye - y);
gdk_flush ();
}
}
}
void
gtk_preview_put_row (GtkPreview *preview,
guchar *src,
guchar *dest,
gint x,
gint y,
gint w)
{
g_return_if_fail (preview != NULL);
g_return_if_fail (GTK_IS_PREVIEW (preview));
g_return_if_fail (src != NULL);
g_return_if_fail (dest != NULL);
switch (preview->type)
{
case GTK_PREVIEW_COLOR:
switch (preview_class->info.visual->depth)
{
case 8:
gtk_color_8 (src, dest, x, y, w);
break;
case 15:
case 16:
gtk_color_16 (src, dest, w);
break;
case 24:
case 32:
gtk_color_24 (src, dest, w);
break;
}
break;
case GTK_PREVIEW_GRAYSCALE:
switch (preview_class->info.visual->depth)
{
case 8:
gtk_grayscale_8 (src, dest, x, y, w);
break;
case 15:
case 16:
gtk_grayscale_16 (src, dest, w);
break;
case 24:
case 32:
gtk_grayscale_24 (src, dest, w);
break;
}
break;
}
}
void
gtk_preview_draw_row (GtkPreview *preview,
guchar *data,
gint x,
gint y,
gint w)
{
guchar *dest;
g_return_if_fail (preview != NULL);
g_return_if_fail (GTK_IS_PREVIEW (preview));
g_return_if_fail (data != NULL);
g_return_if_fail (preview_class->info.visual != NULL);
1997-11-24 22:37:52 +00:00
if ((w <= 0) || (y < 0))
return;
g_return_if_fail (data != NULL);
gtk_preview_make_buffer (preview);
if (y >= preview->buffer_height)
return;
switch (preview->type)
{
case GTK_PREVIEW_COLOR:
switch (preview_class->info.visual->depth)
{
case 8:
dest = preview->buffer + ((gulong) y * (gulong) preview->buffer_width + (gulong) x);
gtk_color_8 (data, dest, x, y, w);
break;
case 15:
case 16:
dest = preview->buffer + ((gulong) y * (gulong) preview->buffer_width + (gulong) x) * 2;
gtk_color_16 (data, dest, w);
break;
case 24:
case 32:
dest = preview->buffer + ((gulong) y * (gulong) preview->buffer_width + (gulong) x) * 3;
gtk_color_24 (data, dest, w);
break;
}
break;
case GTK_PREVIEW_GRAYSCALE:
switch (preview_class->info.visual->depth)
{
case 8:
dest = preview->buffer + ((gulong) y * (gulong) preview->buffer_width + (gulong) x);
gtk_grayscale_8 (data, dest, x, y, w);
break;
case 15:
case 16:
dest = preview->buffer + ((gulong) y * (gulong) preview->buffer_width + (gulong) x) * 2;
gtk_grayscale_16 (data, dest, w);
break;
case 24:
case 32:
dest = preview->buffer + ((gulong) y * (gulong) preview->buffer_width + (gulong) x) * 3;
gtk_grayscale_24 (data, dest, w);
break;
}
break;
}
}
void
gtk_preview_set_expand (GtkPreview *preview,
gint expand)
{
g_return_if_fail (preview != NULL);
g_return_if_fail (GTK_IS_PREVIEW (preview));
preview->expand = (expand != FALSE);
}
void
gtk_preview_set_gamma (double _gamma)
{
if (!preview_info)
{
preview_info = g_new0 (GtkPreviewInfo, 1);
preview_info->nred_shades = 6;
preview_info->ngreen_shades = 6;
preview_info->nblue_shades = 4;
preview_info->ngray_shades = 24;
}
preview_info->gamma = _gamma;
}
void
gtk_preview_set_color_cube (guint nred_shades,
guint ngreen_shades,
guint nblue_shades,
guint ngray_shades)
{
if (!preview_info)
{
preview_info = g_new0 (GtkPreviewInfo, 1);
preview_info->gamma = 1.0;
}
preview_info->nred_shades = nred_shades;
preview_info->ngreen_shades = ngreen_shades;
preview_info->nblue_shades = nblue_shades;
preview_info->ngray_shades = ngray_shades;
}
void
gtk_preview_set_install_cmap (gint _install_cmap)
{
install_cmap = _install_cmap;
}
void
gtk_preview_set_reserved (gint nreserved)
{
if (!preview_info)
preview_info = g_new0 (GtkPreviewInfo, 1);
preview_info->nreserved = nreserved;
}
GdkVisual*
gtk_preview_get_visual ()
{
if (!preview_class)
preview_class = gtk_type_class (gtk_preview_get_type ());
return preview_class->info.visual;
}
GdkColormap*
gtk_preview_get_cmap ()
{
if (!preview_class)
preview_class = gtk_type_class (gtk_preview_get_type ());
return preview_class->info.cmap;
}
GtkPreviewInfo*
gtk_preview_get_info ()
{
if (!preview_class)
preview_class = gtk_type_class (gtk_preview_get_type ());
return &preview_class->info;
}
static void
gtk_preview_finalize (GtkObject *object)
1997-11-24 22:37:52 +00:00
{
GtkPreview *preview;
g_return_if_fail (object != NULL);
g_return_if_fail (GTK_IS_PREVIEW (object));
preview = GTK_PREVIEW (object);
if (preview->buffer)
g_free (preview->buffer);
preview->type = (GtkPreviewType) -1;
(* GTK_OBJECT_CLASS (parent_class)->finalize) (object);
1997-11-24 22:37:52 +00:00
}
static void
gtk_preview_realize (GtkWidget *widget)
{
GtkPreview *preview;
GdkWindowAttr attributes;
gint attributes_mask;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_PREVIEW (widget));
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
preview = GTK_PREVIEW (widget);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = widget->allocation.x;
attributes.y = widget->allocation.y;
attributes.width = widget->allocation.width;
attributes.height = widget->allocation.height;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual (widget);
attributes.colormap = gtk_widget_get_colormap (widget);
attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1997-11-24 22:37:52 +00:00
gdk_window_set_user_data (widget->window, widget);
widget->style = gtk_style_attach (widget->style, widget->window);
gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
}
static gint
gtk_preview_expose (GtkWidget *widget,
GdkEventExpose *event)
{
GtkPreview *preview;
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_PREVIEW (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
if (GTK_WIDGET_DRAWABLE (widget))
{
preview = GTK_PREVIEW (widget);
1997-11-24 22:37:52 +00:00
gtk_preview_put (GTK_PREVIEW (widget),
widget->window, widget->style->black_gc,
event->area.x -
(widget->allocation.width - preview->buffer_width)/2,
event->area.y -
(widget->allocation.height - preview->buffer_height)/2,
1997-11-24 22:37:52 +00:00
event->area.x, event->area.y,
event->area.width, event->area.height);
}
1997-11-24 22:37:52 +00:00
return FALSE;
}
static void
gtk_preview_make_buffer (GtkPreview *preview)
{
GtkWidget *widget;
gint width;
gint height;
g_return_if_fail (preview != NULL);
g_return_if_fail (GTK_IS_PREVIEW (preview));
widget = GTK_WIDGET (preview);
if (preview->expand &&
(widget->allocation.width != 0) &&
(widget->allocation.height != 0))
{
width = widget->allocation.width;
height = widget->allocation.height;
}
else
{
width = widget->requisition.width;
height = widget->requisition.height;
}
if (!preview->buffer ||
(preview->buffer_width != width) ||
(preview->buffer_height != height))
{
if (preview->buffer)
g_free (preview->buffer);
preview->buffer_width = width;
preview->buffer_height = height;
preview->buffer = g_new0 (guchar,
preview->buffer_width *
preview->buffer_height *
preview_class->info.bpp);
}
}
static void
gtk_preview_get_visuals (GtkPreviewClass *klass)
{
static GdkVisualType types[] =
{
GDK_VISUAL_TRUE_COLOR,
GDK_VISUAL_DIRECT_COLOR,
GDK_VISUAL_TRUE_COLOR,
GDK_VISUAL_DIRECT_COLOR,
GDK_VISUAL_TRUE_COLOR,
GDK_VISUAL_DIRECT_COLOR,
GDK_VISUAL_TRUE_COLOR,
GDK_VISUAL_DIRECT_COLOR,
GDK_VISUAL_PSEUDO_COLOR
};
static gint depths[] = { 24, 24, 32, 32, 16, 16, 15, 15, 8 };
static gint nvisual_types = sizeof (types) / sizeof (types[0]);
int i;
g_return_if_fail (klass != NULL);
if (!klass->info.visual)
for (i = 0; i < nvisual_types; i++)
if ((klass->info.visual = gdk_visual_get_best_with_both (depths[i], types[i])))
{
if ((klass->info.visual->type == GDK_VISUAL_TRUE_COLOR) ||
(klass->info.visual->type == GDK_VISUAL_DIRECT_COLOR))
{
klass->info.lookup_red = g_new (gulong, 256);
klass->info.lookup_green = g_new (gulong, 256);
klass->info.lookup_blue = g_new (gulong, 256);
gtk_fill_lookup_array (klass->info.lookup_red,
klass->info.visual->depth,
klass->info.visual->red_shift,
8 - klass->info.visual->red_prec);
gtk_fill_lookup_array (klass->info.lookup_green,
klass->info.visual->depth,
klass->info.visual->green_shift,
8 - klass->info.visual->green_prec);
gtk_fill_lookup_array (klass->info.lookup_blue,
klass->info.visual->depth,
klass->info.visual->blue_shift,
8 - klass->info.visual->blue_prec);
}
break;
}
if (!klass->info.visual)
{
g_warning ("unable to find a suitable visual for color image display.\n");
return;
}
/* If we are _not_ running with an installed cmap, we must run
* with the system visual. Otherwise, we let GDK pick the visual,
* and it makes some effort to pick a non-default visual, which
* will hopefully provide minimum color flashing.
*/
if ((klass->info.visual->depth == gdk_visual_get_system()->depth) &&
(klass->info.visual->type == gdk_visual_get_system()->type) &&
!install_cmap)
{
klass->info.visual = gdk_visual_get_system();
}
1997-11-24 22:37:52 +00:00
switch (klass->info.visual->depth)
{
case 8:
klass->info.bpp = 1;
break;
case 15:
case 16:
klass->info.bpp = 2;
break;
case 24:
case 32:
klass->info.bpp = 3;
break;
}
}
static void
gtk_preview_get_cmaps (GtkPreviewClass *klass)
{
g_return_if_fail (klass != NULL);
g_return_if_fail (klass->info.visual != NULL);
if ((klass->info.visual->type != GDK_VISUAL_TRUE_COLOR) &&
(klass->info.visual->type != GDK_VISUAL_DIRECT_COLOR))
{
if (install_cmap)
{
klass->info.cmap = gdk_colormap_new (klass->info.visual, FALSE);
klass->info.cmap_alloced = install_cmap;
gtk_trim_cmap (klass);
gtk_create_8_bit (klass);
}
else
{
guint nred;
guint ngreen;
guint nblue;
guint ngray;
gint set_prop;
klass->info.cmap = gdk_colormap_get_system ();
set_prop = TRUE;
if (gtk_get_preview_prop (&nred, &ngreen, &nblue, &ngray))
{
set_prop = FALSE;
klass->info.nred_shades = nred;
klass->info.ngreen_shades = ngreen;
klass->info.nblue_shades = nblue;
klass->info.ngray_shades = ngray;
if (klass->info.nreserved)
{
klass->info.reserved_pixels = g_new (gulong, klass->info.nreserved);
if (!gdk_colors_alloc (klass->info.cmap, 0, NULL, 0,
klass->info.reserved_pixels,
klass->info.nreserved))
{
g_free (klass->info.reserved_pixels);
klass->info.reserved_pixels = NULL;
}
}
}
else
{
gtk_trim_cmap (klass);
}
gtk_create_8_bit (klass);
if (set_prop)
gtk_set_preview_prop (klass->info.nred_shades,
klass->info.ngreen_shades,
klass->info.nblue_shades,
klass->info.ngray_shades);
}
}
else
{
if (klass->info.visual == gdk_visual_get_system ())
klass->info.cmap = gdk_colormap_get_system ();
else
{
klass->info.cmap = gdk_colormap_new (klass->info.visual, FALSE);
klass->info.cmap_alloced = TRUE;
}
1997-11-24 22:37:52 +00:00
klass->info.nred_shades = 0;
klass->info.ngreen_shades = 0;
klass->info.nblue_shades = 0;
klass->info.ngray_shades = 0;
}
}
static void
gtk_preview_dither_init (GtkPreviewClass *klass)
{
int i, j, k;
unsigned char low_shade, high_shade;
unsigned short index;
long red_mult, green_mult;
double red_matrix_width;
double green_matrix_width;
double blue_matrix_width;
double gray_matrix_width;
double red_colors_per_shade;
double green_colors_per_shade;
double blue_colors_per_shade;
double gray_colors_per_shade;
gulong *gray_pixels;
gint shades_r, shades_g, shades_b, shades_gray;
GtkDitherInfo *red_ordered_dither;
GtkDitherInfo *green_ordered_dither;
GtkDitherInfo *blue_ordered_dither;
GtkDitherInfo *gray_ordered_dither;
guchar ***dither_matrix;
guchar DM[8][8] =
{
{ 0, 32, 8, 40, 2, 34, 10, 42 },
{ 48, 16, 56, 24, 50, 18, 58, 26 },
{ 12, 44, 4, 36, 14, 46, 6, 38 },
{ 60, 28, 52, 20, 62, 30, 54, 22 },
{ 3, 35, 11, 43, 1, 33, 9, 41 },
{ 51, 19, 59, 27, 49, 17, 57, 25 },
{ 15, 47, 7, 39, 13, 45, 5, 37 },
{ 63, 31, 55, 23, 61, 29, 53, 21 }
};
if (!klass->info.visual || klass->info.visual->type != GDK_VISUAL_PSEUDO_COLOR)
1997-11-24 22:37:52 +00:00
return;
shades_r = klass->info.nred_shades;
shades_g = klass->info.ngreen_shades;
shades_b = klass->info.nblue_shades;
shades_gray = klass->info.ngray_shades;
red_mult = shades_g * shades_b;
green_mult = shades_b;
red_colors_per_shade = 255.0 / (shades_r - 1);
red_matrix_width = red_colors_per_shade / 64;
green_colors_per_shade = 255.0 / (shades_g - 1);
green_matrix_width = green_colors_per_shade / 64;
blue_colors_per_shade = 255.0 / (shades_b - 1);
blue_matrix_width = blue_colors_per_shade / 64;
gray_colors_per_shade = 255.0 / (shades_gray - 1);
gray_matrix_width = gray_colors_per_shade / 64;
/* alloc the ordered dither arrays for accelerated dithering */
klass->info.dither_red = g_new (GtkDitherInfo, 256);
klass->info.dither_green = g_new (GtkDitherInfo, 256);
klass->info.dither_blue = g_new (GtkDitherInfo, 256);
klass->info.dither_gray = g_new (GtkDitherInfo, 256);
red_ordered_dither = klass->info.dither_red;
green_ordered_dither = klass->info.dither_green;
blue_ordered_dither = klass->info.dither_blue;
gray_ordered_dither = klass->info.dither_gray;
dither_matrix = g_new (guchar**, 8);
for (i = 0; i < 8; i++)
{
dither_matrix[i] = g_new (guchar*, 8);
for (j = 0; j < 8; j++)
dither_matrix[i][j] = g_new (guchar, 65);
}
klass->info.dither_matrix = dither_matrix;
/* setup the ordered_dither_matrices */
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
for (k = 0; k <= 64; k++)
dither_matrix[i][j][k] = (DM[i][j] < k) ? 1 : 0;
/* setup arrays containing three bytes of information for red, green, & blue */
/* the arrays contain :
* 1st byte: low end shade value
* 2nd byte: high end shade value
* 3rd & 4th bytes: ordered dither matrix index
*/
gray_pixels = klass->info.gray_pixels;
for (i = 0; i < 256; i++)
{
/* setup the red information */
{
low_shade = (unsigned char) (i / red_colors_per_shade);
if (low_shade == (shades_r - 1))
low_shade--;
high_shade = low_shade + 1;
index = (unsigned short)
(((double) i - low_shade * red_colors_per_shade) /
red_matrix_width);
low_shade *= red_mult;
high_shade *= red_mult;
red_ordered_dither[i].s[1] = index;
red_ordered_dither[i].c[0] = low_shade;
red_ordered_dither[i].c[1] = high_shade;
}
/* setup the green information */
{
low_shade = (unsigned char) (i / green_colors_per_shade);
if (low_shade == (shades_g - 1))
low_shade--;
high_shade = low_shade + 1;
index = (unsigned short)
(((double) i - low_shade * green_colors_per_shade) /
green_matrix_width);
low_shade *= green_mult;
high_shade *= green_mult;
green_ordered_dither[i].s[1] = index;
green_ordered_dither[i].c[0] = low_shade;
green_ordered_dither[i].c[1] = high_shade;
}
/* setup the blue information */
{
low_shade = (unsigned char) (i / blue_colors_per_shade);
if (low_shade == (shades_b - 1))
low_shade--;
high_shade = low_shade + 1;
index = (unsigned short)
(((double) i - low_shade * blue_colors_per_shade) /
blue_matrix_width);
blue_ordered_dither[i].s[1] = index;
blue_ordered_dither[i].c[0] = low_shade;
blue_ordered_dither[i].c[1] = high_shade;
}
/* setup the gray information */
{
low_shade = (unsigned char) (i / gray_colors_per_shade);
if (low_shade == (shades_gray - 1))
low_shade--;
high_shade = low_shade + 1;
index = (unsigned short)
(((double) i - low_shade * gray_colors_per_shade) /
gray_matrix_width);
gray_ordered_dither[i].s[1] = index;
gray_ordered_dither[i].c[0] = gray_pixels[low_shade];
gray_ordered_dither[i].c[1] = gray_pixels[high_shade];
}
}
}
static void
gtk_fill_lookup_array (gulong *array,
int depth,
int shift,
int prec)
{
double one_over_gamma;
double ind;
int val;
int i;
if (preview_class->info.gamma != 0.0)
one_over_gamma = 1.0 / preview_class->info.gamma;
else
one_over_gamma = 1.0;
for (i = 0; i < 256; i++)
{
if (one_over_gamma == 1.0)
array[i] = ((i >> prec) << shift);
else
{
ind = (double) i / 255.0;
val = (int) (255 * pow (ind, one_over_gamma));
array[i] = ((val >> prec) << shift);
}
}
}
static void
gtk_trim_cmap (GtkPreviewClass *klass)
{
gulong pixels[256];
guint nred;
guint ngreen;
guint nblue;
guint ngray;
guint nreserved;
guint total;
guint tmp;
gint success;
nred = klass->info.nred_shades;
ngreen = klass->info.ngreen_shades;
nblue = klass->info.nblue_shades;
ngray = klass->info.ngray_shades;
nreserved = klass->info.nreserved;
success = FALSE;
while (!success)
{
total = nred * ngreen * nblue + ngray + nreserved;
if (total <= 256)
{
if ((nred < 2) || (ngreen < 2) || (nblue < 2) || (ngray < 2))
success = TRUE;
else
{
success = gdk_colors_alloc (klass->info.cmap, 0, NULL, 0, pixels, total);
if (success)
{
if (nreserved > 0)
{
klass->info.reserved_pixels = g_new (gulong, nreserved);
memcpy (klass->info.reserved_pixels, pixels, sizeof (gulong) * nreserved);
gdk_colors_free (klass->info.cmap, &pixels[nreserved],
total - nreserved, 0);
}
else
{
gdk_colors_free (klass->info.cmap, pixels, total, 0);
}
}
}
}
if (!success)
{
if ((nblue >= nred) && (nblue >= ngreen))
nblue = nblue - 1;
else if ((nred >= ngreen) && (nred >= nblue))
nred = nred - 1;
else
{
tmp = log (ngray) / log (2);
if (ngreen >= tmp)
ngreen = ngreen - 1;
else
ngray -= 1;
}
}
}
if ((nred < 2) || (ngreen < 2) || (nblue < 2) || (ngray < 2))
{
g_print ("Unable to allocate sufficient colormap entries.\n");
g_print ("Try exiting other color intensive applications.\n");
return;
}
/* If any of the shade values has changed, issue a warning */
if ((nred != klass->info.nred_shades) ||
(ngreen != klass->info.ngreen_shades) ||
(nblue != klass->info.nblue_shades) ||
(ngray != klass->info.ngray_shades))
{
g_print ("Not enough colors to satisfy requested color cube.\n");
g_print ("Reduced color cube shades from\n");
g_print ("[%d of Red, %d of Green, %d of Blue, %d of Gray] ==> [%d of Red, %d of Green, %d of Blue, %d of Gray]\n",
klass->info.nred_shades, klass->info.ngreen_shades,
klass->info.nblue_shades, klass->info.ngray_shades,
nred, ngreen, nblue, ngray);
}
klass->info.nred_shades = nred;
klass->info.ngreen_shades = ngreen;
klass->info.nblue_shades = nblue;
klass->info.ngray_shades = ngray;
}
static void
gtk_create_8_bit (GtkPreviewClass *klass)
{
unsigned int r, g, b;
unsigned int rv, gv, bv;
unsigned int dr, dg, db, dgray;
GdkColor color;
gulong *pixels;
double one_over_gamma;
int i;
if (!klass->info.color_pixels)
klass->info.color_pixels = g_new (gulong, 256);
if (!klass->info.gray_pixels)
klass->info.gray_pixels = g_new (gulong, 256);
if (klass->info.gamma != 0.0)
one_over_gamma = 1.0 / klass->info.gamma;
else
one_over_gamma = 1.0;
dr = klass->info.nred_shades - 1;
dg = klass->info.ngreen_shades - 1;
db = klass->info.nblue_shades - 1;
dgray = klass->info.ngray_shades - 1;
pixels = klass->info.color_pixels;
for (r = 0, i = 0; r <= dr; r++)
for (g = 0; g <= dg; g++)
for (b = 0; b <= db; b++, i++)
{
rv = (unsigned int) ((r * klass->info.visual->colormap_size) / dr);
gv = (unsigned int) ((g * klass->info.visual->colormap_size) / dg);
bv = (unsigned int) ((b * klass->info.visual->colormap_size) / db);
color.red = ((int) (255 * pow ((double) rv / 256.0, one_over_gamma))) * 257;
color.green = ((int) (255 * pow ((double) gv / 256.0, one_over_gamma))) * 257;
color.blue = ((int) (255 * pow ((double) bv / 256.0, one_over_gamma))) * 257;
if (!gdk_color_alloc (klass->info.cmap, &color))
{
g_error ("could not initialize 8-bit combined colormap");
return;
}
pixels[i] = color.pixel;
}
1997-11-24 22:37:52 +00:00
pixels = klass->info.gray_pixels;
for (i = 0; i < (int) klass->info.ngray_shades; i++)
{
color.red = (i * klass->info.visual->colormap_size) / dgray;
color.red = ((int) (255 * pow ((double) color.red / 256.0, one_over_gamma))) * 257;
color.green = color.red;
color.blue = color.red;
if (!gdk_color_alloc (klass->info.cmap, &color))
{
g_error ("could not initialize 8-bit combined colormap");
return;
}
pixels[i] = color.pixel;
}
}
static void
gtk_color_8 (guchar *src,
guchar *dest,
gint x,
gint y,
gulong width)
{
gulong *colors;
GtkDitherInfo *dither_red;
GtkDitherInfo *dither_green;
GtkDitherInfo *dither_blue;
GtkDitherInfo r, g, b;
guchar **dither_matrix;
guchar *matrix;
colors = preview_class->info.color_pixels;
dither_red = preview_class->info.dither_red;
dither_green = preview_class->info.dither_green;
dither_blue = preview_class->info.dither_blue;
dither_matrix = preview_class->info.dither_matrix[y & 0x7];
while (width--)
{
r = dither_red[src[0]];
g = dither_green[src[1]];
b = dither_blue[src[2]];
src += 3;
matrix = dither_matrix[x++ & 0x7];
*dest++ = colors[(r.c[matrix[r.s[1]]] +
g.c[matrix[g.s[1]]] +
b.c[matrix[b.s[1]]])];
}
}
static void
gtk_color_16 (guchar *src,
guchar *dest,
gulong width)
{
gulong *lookup_red;
gulong *lookup_green;
gulong *lookup_blue;
gulong val;
lookup_red = preview_class->info.lookup_red;
lookup_green = preview_class->info.lookup_green;
lookup_blue = preview_class->info.lookup_blue;
while (width--)
{
val = COLOR_COMPOSE (src[0], src[1], src[2]);
dest[0] = val;
dest[1] = val >> 8;
dest += 2;
src += 3;
}
}
static void
gtk_color_24 (guchar *src,
guchar *dest,
gulong width)
{
gulong *lookup_red;
gulong *lookup_green;
gulong *lookup_blue;
gulong val;
lookup_red = preview_class->info.lookup_red;
lookup_green = preview_class->info.lookup_green;
lookup_blue = preview_class->info.lookup_blue;
while (width--)
{
val = COLOR_COMPOSE (src[0], src[1], src[2]);
dest[0] = val;
dest[1] = val >> 8;
dest[2] = val >> 16;
dest += 3;
src += 3;
}
}
static void
gtk_grayscale_8 (guchar *src,
guchar *dest,
gint x,
gint y,
gulong width)
{
GtkDitherInfo *dither_gray;
GtkDitherInfo gray;
guchar **dither_matrix;
guchar *matrix;
dither_gray = preview_class->info.dither_gray;
dither_matrix = preview_class->info.dither_matrix[y & 0x7];
while (width--)
{
gray = dither_gray[*src++];
matrix = dither_matrix[x++ & 0x7];
*dest++ = gray.c[matrix[gray.s[1]]];
}
}
static void
gtk_grayscale_16 (guchar *src,
guchar *dest,
gulong width)
{
gulong *lookup_red;
gulong *lookup_green;
gulong *lookup_blue;
gulong val;
lookup_red = preview_class->info.lookup_red;
lookup_green = preview_class->info.lookup_green;
lookup_blue = preview_class->info.lookup_blue;
while (width--)
{
val = COLOR_COMPOSE (*src, *src, *src);
dest[0] = val;
dest[1] = val >> 8;
dest += 2;
src += 1;
}
}
static void
gtk_grayscale_24 (guchar *src,
guchar *dest,
gulong width)
{
gulong *lookup_red;
gulong *lookup_green;
gulong *lookup_blue;
gulong val;
lookup_red = preview_class->info.lookup_red;
lookup_green = preview_class->info.lookup_green;
lookup_blue = preview_class->info.lookup_blue;
while (width--)
{
val = COLOR_COMPOSE (*src, *src, *src);
dest[0] = val;
dest[1] = val >> 8;
dest[2] = val >> 16;
dest += 3;
src += 1;
}
}
static gint
gtk_get_preview_prop (guint *nred,
guint *ngreen,
guint *nblue,
guint *ngray)
{
GtkPreviewProp *prop;
GdkAtom property;
/* FIXME: need to grab the server here to prevent a race condition */
1997-11-24 22:37:52 +00:00
property = gdk_atom_intern ("GTK_PREVIEW_INFO", FALSE);
if (gdk_property_get (NULL, property, property,
0, sizeof (GtkPreviewProp), FALSE,
NULL, NULL, NULL, (guchar**) &prop))
{
*nred = prop->nred_shades;
*ngreen = prop->ngreen_shades;
*nblue = prop->nblue_shades;
*ngray = prop->ngray_shades;
1997-11-24 22:37:52 +00:00
prop->ref_count = prop->ref_count + 1;
1997-11-24 22:37:52 +00:00
gdk_property_change (NULL, property, property, 16,
GDK_PROP_MODE_REPLACE,
(guchar*) prop, 5);
return TRUE;
}
return FALSE;
}
static void
gtk_set_preview_prop (guint nred,
guint ngreen,
guint nblue,
guint ngray)
{
GtkPreviewProp prop;
GdkAtom property;
property = gdk_atom_intern ("GTK_PREVIEW_INFO", FALSE);
prop.ref_count = 1;
prop.nred_shades = nred;
prop.ngreen_shades = ngreen;
prop.nblue_shades = nblue;
prop.ngray_shades = ngray;
1997-11-24 22:37:52 +00:00
gdk_property_change (NULL, property, property, 16,
GDK_PROP_MODE_REPLACE,
(guchar*) &prop, 5);
}
static void
gtk_lsbmsb_1_1 (guchar *dest,
guchar *src,
gint count)
{
memcpy (dest, src, count);
}
static void
gtk_lsb_2_2 (guchar *dest,
guchar *src,
gint count)
{
memcpy (dest, src, count * 2);
}
static void
gtk_msb_2_2 (guchar *dest,
guchar *src,
gint count)
{
while (count--)
{
dest[0] = src[1];
dest[1] = src[0];
dest += 2;
src += 2;
}
}
static void
gtk_lsb_3_3 (guchar *dest,
guchar *src,
gint count)
{
memcpy (dest, src, count * 3);
}
static void
gtk_msb_3_3 (guchar *dest,
guchar *src,
gint count)
{
while (count--)
{
dest[0] = src[2];
dest[1] = src[1];
dest[2] = src[0];
dest += 3;
src += 3;
}
}
static void
gtk_lsb_3_4 (guchar *dest,
guchar *src,
gint count)
{
while (count--)
{
dest[0] = src[0];
dest[1] = src[1];
dest[2] = src[2];
dest += 4;
src += 3;
}
}
static void
gtk_msb_3_4 (guchar *dest,
guchar *src,
gint count)
{
while (count--)
{
dest[1] = src[2];
dest[2] = src[1];
dest[3] = src[0];
dest += 4;
src += 3;
}
}