Files
gimp/plug-ins/imagemap/imap_preview.c
Ell f91e2a94a7 imagemap: clear preview area background before drawing image
Previously, we neither cleared the preview area's buffer, nor set
its max size, so if the preview area was bigger than the image, the
rest of the widget would display garbage.

This is an ad-hoc-ish fix, simply filling the preview area's buffer
with the background color prior to drawing the image.  Anything
more sophisticated will have to wait till image maps are cool
again, I guess :)
2016-12-20 14:13:58 -05:00

572 lines
17 KiB
C

/*
* This is a plug-in for GIMP.
*
* Generates clickable image maps.
*
* Copyright (C) 1998-2005 Maurits Rijk m.rijk@chello.nl
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
#include "imap_commands.h"
#include "imap_grid.h"
#include "imap_main.h"
#include "imap_menu.h"
#include "imap_preview.h"
#define PREVIEW_MASK (GDK_EXPOSURE_MASK | \
GDK_POINTER_MOTION_MASK | \
GDK_BUTTON_PRESS_MASK | \
GDK_BUTTON_RELEASE_MASK | \
GDK_BUTTON_MOTION_MASK | \
GDK_KEY_PRESS_MASK | \
GDK_KEY_RELEASE_MASK | \
GDK_ENTER_NOTIFY_MASK | \
GDK_LEAVE_NOTIFY_MASK)
#define PREVIEW_SIZE 400
/*======================================================================
Preview Rendering Util routine
=======================================================================*/
#define CHECKWIDTH 4
#define LIGHTCHECK 192
#define DARKCHECK 128
#ifndef OPAQUE
#define OPAQUE 255
#endif
static Object_t *_tmp_obj;
static Preview_t*
preview_user_data(GtkWidget *preview)
{
return (Preview_t*) g_object_get_data (G_OBJECT (preview), "preview");
}
gint
preview_get_width(GtkWidget *preview)
{
return preview_user_data(preview)->width;
}
gint
preview_get_height(GtkWidget *preview)
{
return preview_user_data(preview)->height;
}
static void
render_background(Preview_t *preview_base)
{
GtkWidget *preview = preview_base->preview;
GtkStyle *style;
const GdkColor *bg_color;
gtk_widget_ensure_style (preview);
style = gtk_widget_get_style (preview);
bg_color = &style->bg[GTK_STATE_NORMAL];
gimp_preview_area_fill (GIMP_PREVIEW_AREA (preview),
0, 0, G_MAXINT, G_MAXINT,
bg_color->red >> 8,
bg_color->green >> 8,
bg_color->blue >> 8);
}
static void
render_gray_image(Preview_t *preview_base, GimpPixelRgn *srcrgn)
{
guchar *src_row, *dest_buffer, *dest;
gint row, col;
gint bpp, dwidth, dheight, pwidth, pheight;
gint *src_col;
GtkWidget *preview = preview_base->preview;
dwidth = srcrgn->w;
dheight = srcrgn->h;
pwidth = preview_base->widget_width;
pheight = preview_base->widget_height;
bpp = srcrgn->bpp;
src_row = g_new(guchar, dwidth * bpp);
dest_buffer = g_new(guchar, pwidth * pheight);
src_col = g_new(gint, pwidth);
for (col = 0; col < pwidth; col++)
src_col[col] = (col * dwidth / pwidth) * bpp;
dest = dest_buffer;
for (row = 0; row < pheight; row++) {
gimp_pixel_rgn_get_row(srcrgn, src_row, 0, row * dheight / pheight,
dwidth);
for (col = 0; col < pwidth; col++) {
guchar *src;
src = &src_row[src_col[col]];
*dest++ = *src;
}
}
gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
0, 0, pwidth, pheight,
GIMP_GRAY_IMAGE,
dest_buffer,
pwidth);
g_free(src_col);
g_free(src_row);
g_free(dest_buffer);
}
static void
render_indexed_image(Preview_t *preview_base, GimpPixelRgn *srcrgn)
{
guchar *src_row, *dest_buffer, *src, *dest;
gint row, col;
gint dwidth, dheight, pwidth, pheight;
gint *src_col;
gint bpp, alpha, has_alpha;
guchar *cmap, *color;
gint ncols;
gboolean gray = get_map_info()->show_gray;
GtkWidget *preview = preview_base->preview;
dwidth = srcrgn->w;
dheight = srcrgn->h;
pwidth = preview_base->widget_width;
pheight = preview_base->widget_height;
bpp = srcrgn->bpp;
alpha = bpp;
has_alpha = gimp_drawable_has_alpha(srcrgn->drawable->drawable_id);
if (has_alpha)
alpha--;
cmap = gimp_image_get_colormap (gimp_item_get_image (srcrgn->drawable->drawable_id),
&ncols);
src_row = g_new(guchar, dwidth * bpp);
dest_buffer = g_new(guchar, pwidth * pheight * 3);
src_col = g_new(gint, pwidth);
for (col = 0; col < pwidth; col++)
src_col[col] = (col * dwidth / pwidth) * bpp;
dest = dest_buffer;
for (row = 0; row < pheight; row++) {
gimp_pixel_rgn_get_row(srcrgn, src_row, 0, row * dheight / pheight,
dwidth);
for (col = 0; col < pwidth; col++) {
src = &src_row[src_col[col]];
color = cmap + 3 * (int)(*src);
if (gray) {
guchar avg = (299 * color[0] + 587 * color[1] +
114 * color[2]) / 1000;
*dest++ = avg;
*dest++ = avg;
*dest++ = avg;
} else {
*dest++ = color[0];
*dest++ = color[1];
*dest++ = color[2];
}
}
}
gimp_preview_area_draw(GIMP_PREVIEW_AREA(preview),
0, 0, pwidth, pheight,
GIMP_RGB_IMAGE,
dest_buffer,
pwidth * 3);
g_free(src_col);
g_free(src_row);
g_free(dest_buffer);
}
static void
render_rgb_image(Preview_t *preview_base, GimpPixelRgn *srcrgn)
{
guchar *src_row, *dest_buffer, *src, *dest;
gint row, col;
gint dwidth, dheight, pwidth, pheight;
gint *src_col;
gint bpp, alpha, has_alpha, b;
guchar check;
gboolean gray = get_map_info()->show_gray;
GtkWidget *preview = preview_base->preview;
dwidth = srcrgn->w;
dheight = srcrgn->h;
pwidth = preview_base->widget_width;
pheight = preview_base->widget_height;
bpp = srcrgn->bpp;
alpha = bpp;
has_alpha = gimp_drawable_has_alpha(srcrgn->drawable->drawable_id);
if (has_alpha)
alpha--;
src_row = g_new(guchar, dwidth * bpp);
dest_buffer = g_new(guchar, pwidth * pheight * bpp);
src_col = g_new(gint, pwidth);
for (col = 0; col < pwidth; col++)
src_col[col] = (col * dwidth / pwidth) * bpp;
dest = dest_buffer;
for (row = 0; row < pheight; row++) {
gimp_pixel_rgn_get_row(srcrgn, src_row, 0, row * dheight / pheight,
dwidth);
for (col = 0; col < pwidth; col++) {
src = &src_row[src_col[col]];
if(!has_alpha || src[alpha] == OPAQUE) {
/* no alpha channel or opaque -- simple way */
for (b = 0; b < alpha; b++)
dest[b] = src[b];
} else {
/* more or less transparent */
if( ( col % (CHECKWIDTH*2) < CHECKWIDTH ) ^
( row % (CHECKWIDTH*2) < CHECKWIDTH ) )
check = LIGHTCHECK;
else
check = DARKCHECK;
if (src[alpha] == 0) {
/* full transparent -- check */
for (b = 0; b < alpha; b++)
dest[b] = check;
} else {
/* middlemost transparent -- mix check and src */
for (b = 0; b < alpha; b++)
dest[b] = (src[b] * src[alpha] +
check * (OPAQUE - src[alpha])) / OPAQUE;
}
}
if (gray) {
guchar avg;
avg = (299 * dest[0] + 587 * dest[1] + 114 * dest[2]) / 1000;
for (b = 0; b < alpha; b++)
dest[b] = avg;
}
dest += alpha;
}
}
gimp_preview_area_draw (GIMP_PREVIEW_AREA (preview),
0, 0, pwidth, pheight,
GIMP_RGB_IMAGE,
dest_buffer,
pwidth * 3);
g_free(src_col);
g_free(src_row);
g_free(dest_buffer);
}
static void
render_preview(Preview_t *preview_base, GimpPixelRgn *srcrgn)
{
render_background (preview_base);
switch (gimp_drawable_type(srcrgn->drawable->drawable_id)) {
case GIMP_RGB_IMAGE:
case GIMP_RGBA_IMAGE:
render_rgb_image(preview_base, srcrgn);
break;
case GIMP_GRAY_IMAGE:
case GIMP_GRAYA_IMAGE:
render_gray_image(preview_base, srcrgn);
break;
case GIMP_INDEXED_IMAGE:
case GIMP_INDEXEDA_IMAGE:
render_indexed_image(preview_base, srcrgn);
break;
}
}
static gboolean
arrow_cb(GtkWidget *widget, GdkEventButton *event, gpointer data)
{
if (event->button == 1)
do_main_popup_menu(event);
return TRUE;
}
static gboolean
preview_expose(GtkWidget *widget, GdkEventExpose *event)
{
cairo_t *cr;
gint width = preview_get_width (widget);
gint height = preview_get_height (widget);
cr = gdk_cairo_create (event->window);
gdk_cairo_region (cr, event->region);
cairo_clip (cr);
cairo_set_line_width (cr, 1.);
draw_grid (cr, width, height);
draw_shapes (cr);
if (_tmp_obj)
{
/* this is a bit of a hack */
gdouble dash = 4.;
_tmp_obj->selected |= 4;
cairo_set_source_rgb (cr, 1., 0., 1.);
cairo_set_dash (cr, &dash, 1, dash);
object_draw (_tmp_obj, cr);
}
cairo_destroy (cr);
return FALSE;
}
void
preview_set_tmp_obj (Object_t *obj)
{
_tmp_obj = obj;
}
void
preview_unset_tmp_obj (Object_t *obj)
{
if (_tmp_obj == obj) _tmp_obj = NULL;
}
void
preview_zoom(Preview_t *preview, gint zoom_factor)
{
preview->widget_width = preview->width * zoom_factor;
preview->widget_height = preview->height * zoom_factor;
gtk_widget_set_size_request (preview->preview, preview->widget_width,
preview->widget_height);
gtk_widget_queue_resize(preview->window);
render_preview(preview, &preview->src_rgn);
preview_redraw();
}
GdkCursorType
preview_set_cursor(Preview_t *preview, GdkCursorType cursor_type)
{
GdkCursorType prev_cursor = preview->cursor;
GdkDisplay *display = gtk_widget_get_display (preview->window);
GdkCursor *cursor = gdk_cursor_new_for_display (display,
cursor_type);
gdk_window_set_cursor(gtk_widget_get_window (preview->window), cursor);
gdk_cursor_unref(cursor);
preview->cursor = cursor_type;
return prev_cursor;
}
static const GtkTargetEntry target_table[] =
{
{"STRING", 0, 1 },
{"text/plain", 0, 2 }
};
static void
handle_drop(GtkWidget *widget, GdkDragContext *context, gint x, gint y,
GtkSelectionData *data, guint info, guint time)
{
gboolean success = FALSE;
if (gtk_selection_data_get_length (data) >= 0 &&
gtk_selection_data_get_format (data) == 8)
{
ObjectList_t *list = get_shapes();
Object_t *obj;
x = get_real_coord(x);
y = get_real_coord(y);
obj = object_list_find(list, x, y);
if (obj && !obj->locked)
{
command_list_add(edit_object_command_new(obj));
object_set_url(obj, (const gchar *) gtk_selection_data_get_data (data));
object_emit_update_signal(obj);
success = TRUE;
}
}
gtk_drag_finish(context, success, FALSE, time);
}
static void
preview_size_allocate (GtkWidget *widget,
GtkAllocation *allocation,
gpointer preview_void)
{
Preview_t *preview = preview_void;
render_preview (preview, &preview->src_rgn);
}
static void
scroll_adj_changed (GtkAdjustment *adj,
GimpRuler *ruler)
{
gimp_ruler_set_range (ruler,
gtk_adjustment_get_value (adj),
gtk_adjustment_get_value (adj) +
gtk_adjustment_get_page_size (adj),
gtk_adjustment_get_upper (adj));
}
Preview_t *
make_preview (GimpDrawable *drawable)
{
Preview_t *data = g_new(Preview_t, 1);
GtkAdjustment *hadj;
GtkAdjustment *vadj;
GtkWidget *preview;
GtkWidget *window;
GtkWidget *viewport;
GtkWidget *button, *arrow;
GtkWidget *ruler;
GtkWidget *table;
GtkWidget *scrollbar;
gint width, height;
data->drawable = drawable;
data->preview = preview = gimp_preview_area_new ();
g_object_set_data (G_OBJECT (preview), "preview", data);
gtk_widget_set_events (GTK_WIDGET (preview), PREVIEW_MASK);
g_signal_connect_after (preview, "expose-event",
G_CALLBACK (preview_expose),
data);
g_signal_connect (preview, "size-allocate",
G_CALLBACK (preview_size_allocate),
data);
/* Handle drop of links in preview widget */
gtk_drag_dest_set (preview, GTK_DEST_DEFAULT_ALL, target_table,
2, GDK_ACTION_COPY);
g_signal_connect (preview, "drag-data-received",
G_CALLBACK (handle_drop),
NULL);
data->widget_width = data->width =
gimp_drawable_width (drawable->drawable_id);
data->widget_height = data->height =
gimp_drawable_height (drawable->drawable_id);
gtk_widget_set_size_request (preview, data->widget_width,
data->widget_height);
/* The main table */
data->window = table = gtk_table_new (3, 3, FALSE);
gtk_table_set_col_spacings (GTK_TABLE (table), 1);
gtk_table_set_row_spacings (GTK_TABLE (table), 1);
/* Create button with arrow */
button = gtk_button_new ();
gtk_widget_set_can_focus (button, FALSE);
gtk_table_attach (GTK_TABLE (table), button, 0, 1, 0, 1,
GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_set_events (button,
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
gtk_widget_show (button);
g_signal_connect (button, "button-press-event",
G_CALLBACK (arrow_cb),
NULL);
arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
gtk_container_add (GTK_CONTAINER (button), arrow);
gtk_widget_show (arrow);
/* Create horizontal ruler */
data->hruler = ruler = gimp_ruler_new (GTK_ORIENTATION_HORIZONTAL);
g_signal_connect_swapped (preview, "motion-notify-event",
G_CALLBACK (GTK_WIDGET_GET_CLASS (ruler)->motion_notify_event),
ruler);
gtk_table_attach (GTK_TABLE (table), ruler, 1, 2, 0, 1,
GTK_EXPAND | GTK_SHRINK | GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (ruler);
/* Create vertical ruler */
data->vruler = ruler = gimp_ruler_new (GTK_ORIENTATION_VERTICAL);
g_signal_connect_swapped (preview, "motion-notify-event",
G_CALLBACK (GTK_WIDGET_GET_CLASS (ruler)->motion_notify_event),
ruler);
gtk_table_attach (GTK_TABLE (table), ruler, 0, 1, 1, 2,
GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
gtk_widget_show (ruler);
window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (window),
GTK_POLICY_NEVER, GTK_POLICY_NEVER);
width = (data->width > 600) ? 600 : data->width;
height = (data->height > 400) ? 400 : data->height;
gtk_widget_set_size_request (window, width, height);
gtk_table_attach (GTK_TABLE (table), window, 1, 2, 1, 2,
GTK_FILL, GTK_FILL, 0, 0);
gtk_widget_show (window);
viewport = gtk_viewport_new (NULL, NULL);
gtk_container_add (GTK_CONTAINER (window), viewport);
gtk_widget_show (viewport);
hadj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (window));
g_signal_connect (hadj, "changed",
G_CALLBACK (scroll_adj_changed),
data->hruler);
g_signal_connect (hadj, "value-changed",
G_CALLBACK (scroll_adj_changed),
data->hruler);
vadj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (window));
g_signal_connect (vadj, "changed",
G_CALLBACK (scroll_adj_changed),
data->vruler);
g_signal_connect (vadj, "value-changed",
G_CALLBACK (scroll_adj_changed),
data->vruler);
gtk_container_add (GTK_CONTAINER (viewport), preview);
scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_HORIZONTAL, hadj);
gtk_table_attach(GTK_TABLE(table), scrollbar, 1, 2, 2, 3,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
gtk_widget_show (scrollbar);
scrollbar = gtk_scrollbar_new (GTK_ORIENTATION_VERTICAL, vadj);
gtk_table_attach (GTK_TABLE (table), scrollbar, 2, 3, 1, 2,
GTK_FILL | GTK_SHRINK, GTK_FILL | GTK_SHRINK, 0, 0);
gtk_widget_show (scrollbar);
gtk_widget_show (preview);
gimp_pixel_rgn_init (&data->src_rgn, drawable, 0, 0, data->width,
data->height, FALSE, FALSE);
render_preview (data, &data->src_rgn);
gtk_widget_show (table);
return data;
}