/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * GimpContextPreview Widget * Copyright (C) 1999 Sven Neumann * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include "libgimpwidgets/gimpwidgets.h" #include "apptypes.h" #include "brush_scale.h" #include "gimpbrush.h" #include "gimpbrushpipe.h" #include "gimpcontextpreview.h" #include "gimpdnd.h" #include "gimppattern.h" #include "gradient.h" #include "gradient_header.h" #include "patterns.h" #include "temp_buf.h" #include "libgimp/gimplimits.h" /* the pixmaps for the [scale|pipe]_indicators */ #define indicator_width 7 #define indicator_height 7 #define WHT {255, 255, 255} #define BLK { 0, 0, 0} #define RED {255, 127, 127} static unsigned char scale_indicator_bits[7][7][3] = { { WHT, WHT, WHT, WHT, WHT, WHT, WHT }, { WHT, WHT, WHT, BLK, WHT, WHT, WHT }, { WHT, WHT, WHT, BLK, WHT, WHT, WHT }, { WHT, BLK, BLK, BLK, BLK, BLK, WHT }, { WHT, WHT, WHT, BLK, WHT, WHT, WHT }, { WHT, WHT, WHT, BLK, WHT, WHT, WHT }, { WHT, WHT, WHT, WHT, WHT, WHT, WHT } }; static unsigned char pipe_indicator_bits[7][7][3] = { { WHT, WHT, WHT, WHT, WHT, WHT, WHT }, { WHT, WHT, WHT, WHT, WHT, WHT, RED }, { WHT, WHT, WHT, WHT, WHT, RED, RED }, { WHT, WHT, WHT, WHT, RED, RED, RED }, { WHT, WHT, WHT, RED, RED, RED, RED }, { WHT, WHT, RED, RED, RED, RED, RED }, { WHT, RED, RED, RED, RED, RED, RED } }; static unsigned char scale_pipe_indicator_bits[7][7][3] = { { WHT, WHT, WHT, WHT, WHT, WHT, WHT }, { WHT, WHT, WHT, BLK, WHT, WHT, RED }, { WHT, WHT, WHT, BLK, WHT, RED, RED }, { WHT, BLK, BLK, BLK, BLK, BLK, RED }, { WHT, WHT, WHT, BLK, RED, RED, RED }, { WHT, WHT, RED, BLK, RED, RED, RED }, { WHT, RED, RED, RED, RED, RED, RED } }; /* how long to wait after mouse-down before showing popup */ #define POPUP_DELAY_MS 150 /* size of the gradient popup */ #define GRADIENT_POPUP_WIDTH 128 #define GRADIENT_POPUP_HEIGHT 32 #define DRAG_PREVIEW_SIZE 32 /* event mask for the context_preview */ #define CONTEXT_PREVIEW_EVENT_MASK (GDK_BUTTON_PRESS_MASK | \ GDK_BUTTON_RELEASE_MASK | \ GDK_ENTER_NOTIFY_MASK | \ GDK_LEAVE_NOTIFY_MASK) /* shared widgets for the popups */ /* XXX: It is pretty dangerous making these variables statics, * I'd feel safer if they were per-instance and private. -- austin */ static GtkWidget *gcp_popup = NULL; static GtkWidget *gcp_popup_preview = NULL; static guint gcp_pipe_timer = 0; static guint gcp_pipe_index = 0; static guint gcp_popup_timer = 0; /* dnd stuff */ static GtkTargetEntry context_preview_target_table[3][1] = { { GIMP_TARGET_BRUSH }, { GIMP_TARGET_PATTERN }, { GIMP_TARGET_GRADIENT } }; static guint n_targets = 1; /* signals */ enum { CLICKED, LAST_SIGNAL }; static guint gimp_context_preview_signals[LAST_SIGNAL] = { 0 }; static GtkPreviewClass *parent_class = NULL; static gpointer gimp_context_preview_get_data (GimpContextPreview *, gpointer); static gint gimp_context_preview_button_press_event (GtkWidget *, GdkEventButton *); static gint gimp_context_preview_button_release_event (GtkWidget *, GdkEventButton *); static void gimp_context_preview_popup_open (GimpContextPreview *, gint, gint); static void gimp_context_preview_popup_close (void); static gboolean gimp_context_preview_data_matches_type (GimpContextPreview *, gpointer); static void gimp_context_preview_draw_brush (GimpContextPreview *); static void gimp_context_preview_draw_brush_popup (GimpContextPreview *); static gint gimp_context_preview_animate_pipe (GimpContextPreview *); static void gimp_context_preview_draw_pattern (GimpContextPreview *); static void gimp_context_preview_draw_pattern_popup (GimpContextPreview *); static void gimp_context_preview_draw_gradient (GimpContextPreview *); static void gimp_context_preview_draw_gradient_popup (GimpContextPreview *); static gint brush_dirty_callback (GimpBrush *, GimpContextPreview *); static gint brush_rename_callback (GimpBrush *, GimpContextPreview *); static void gimp_context_preview_destroy (GtkObject *object) { g_return_if_fail (GIMP_IS_CONTEXT_PREVIEW (object)); if (GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); } static void gimp_context_preview_class_init (GimpContextPreviewClass *class) { GtkObjectClass *object_class; GtkWidgetClass *widget_class; object_class = (GtkObjectClass*) class; widget_class = (GtkWidgetClass*) class; parent_class = gtk_type_class (gtk_preview_get_type ()); gimp_context_preview_signals[CLICKED] = gtk_signal_new ("clicked", GTK_RUN_FIRST, object_class->type, GTK_SIGNAL_OFFSET (GimpContextPreviewClass, clicked), gtk_signal_default_marshaller, GTK_TYPE_NONE, 0); gtk_object_class_add_signals (object_class, gimp_context_preview_signals, LAST_SIGNAL); class->clicked = NULL; widget_class->button_press_event = gimp_context_preview_button_press_event; widget_class->button_release_event = gimp_context_preview_button_release_event; object_class->destroy = gimp_context_preview_destroy; } static void gimp_context_preview_init (GimpContextPreview *gcp) { gcp->data = NULL; gcp->type = GCP_LAST; gcp->width = 0; gcp->height = 0; gcp->popup_width = 0; gcp->popup_height = 0; gcp->show_popup = FALSE; gcp->show_tooltips = FALSE; GTK_PREVIEW (gcp)->type = GTK_PREVIEW_COLOR; GTK_PREVIEW (gcp)->bpp = 3; GTK_PREVIEW (gcp)->dither = GDK_RGB_DITHER_NORMAL; gtk_widget_set_events (GTK_WIDGET (gcp), CONTEXT_PREVIEW_EVENT_MASK); } GtkType gimp_context_preview_get_type (void) { static GtkType gcp_type = 0; if (!gcp_type) { GtkTypeInfo gcp_info = { "GimpContextPreview", sizeof (GimpContextPreview), sizeof (GimpContextPreviewClass), (GtkClassInitFunc) gimp_context_preview_class_init, (GtkObjectInitFunc) gimp_context_preview_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL }; gcp_type = gtk_type_unique (gtk_preview_get_type (), &gcp_info); } return gcp_type; } GtkWidget * gimp_context_preview_new (GimpContextPreviewType type, gint width, gint height, gboolean show_popup, gboolean show_tooltips, GtkSignalFunc drop_data_callback, gpointer drop_data_data) { GimpContextPreview *gcp; g_return_val_if_fail (type >= 0 && type < GCP_LAST, NULL); g_return_val_if_fail (width > 0 && height > 0, NULL); gcp = gtk_type_new (gimp_context_preview_get_type ()); gcp->type = type; gcp->width = width; gcp->height = height; gcp->show_popup = show_popup; gcp->show_tooltips = show_tooltips; gtk_preview_size (GTK_PREVIEW (gcp), width, height); /* drag dest */ gtk_drag_dest_set (GTK_WIDGET (gcp), GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP, context_preview_target_table[type], n_targets, GDK_ACTION_COPY); switch (type) { case GCP_BRUSH: gimp_dnd_brush_dest_set (GTK_WIDGET (gcp), (GimpDndDropBrushFunc) drop_data_callback, drop_data_data); break; case GCP_PATTERN: gimp_dnd_pattern_dest_set (GTK_WIDGET (gcp), (GimpDndDropPatternFunc) drop_data_callback, drop_data_data); break; case GCP_GRADIENT: gimp_dnd_gradient_dest_set (GTK_WIDGET (gcp), (GimpDndDropGradientFunc) drop_data_callback, drop_data_data); break; default: break; } return GTK_WIDGET (gcp); } void gimp_context_preview_update (GimpContextPreview *gcp, gpointer data) { g_return_if_fail (GIMP_IS_CONTEXT_PREVIEW (gcp)); g_return_if_fail (gimp_context_preview_data_matches_type (gcp, data)); if (!gcp->data) { if (gcp->show_popup) gtk_drag_source_set (GTK_WIDGET (gcp), GDK_BUTTON2_MASK, context_preview_target_table[gcp->type], n_targets, GDK_ACTION_COPY); else gtk_drag_source_set (GTK_WIDGET (gcp), GDK_BUTTON1_MASK | GDK_BUTTON2_MASK, context_preview_target_table[gcp->type], n_targets, GDK_ACTION_COPY); switch (gcp->type) { case GCP_BRUSH: gimp_dnd_brush_source_set (GTK_WIDGET (gcp), (GimpDndDragBrushFunc) gimp_context_preview_get_data, NULL); break; case GCP_PATTERN: gimp_dnd_pattern_source_set (GTK_WIDGET (gcp), (GimpDndDragPatternFunc) gimp_context_preview_get_data, NULL); break; case GCP_GRADIENT: gimp_dnd_gradient_source_set (GTK_WIDGET (gcp), (GimpDndDragGradientFunc) gimp_context_preview_get_data, NULL); break; default: break; } } if (gcp->data && gcp->type == GCP_BRUSH) gtk_signal_disconnect_by_data (GTK_OBJECT (gcp->data), gcp); gcp->data = data; if (GTK_IS_OBJECT (gcp->data)) gtk_signal_connect (GTK_OBJECT (gcp->data), "destroy", gtk_widget_destroyed, &gcp->data); switch (gcp->type) { case GCP_BRUSH: gimp_context_preview_draw_brush (gcp); gtk_signal_connect (GTK_OBJECT (gcp->data), "dirty", GTK_SIGNAL_FUNC (brush_dirty_callback), gcp); gtk_signal_connect (GTK_OBJECT (gcp->data), "name_changed", GTK_SIGNAL_FUNC (brush_rename_callback), gcp); break; case GCP_PATTERN: gimp_context_preview_draw_pattern (gcp); break; case GCP_GRADIENT: gimp_context_preview_draw_gradient (gcp); break; default: break; } gtk_widget_queue_draw (GTK_WIDGET (gcp)); if (gcp->show_tooltips) { gchar *name = NULL; switch (gcp->type) { case GCP_BRUSH: { GimpBrush *brush = GIMP_BRUSH (gcp->data); name = GIMP_OBJECT (brush)->name; } break; case GCP_PATTERN: { GimpPattern *pattern = GIMP_PATTERN (gcp->data); name = GIMP_OBJECT (pattern)->name; } break; case GCP_GRADIENT: { gradient_t *gradient = (gradient_t *) (gcp->data); name = gradient->name; } break; default: break; } gimp_help_set_help_data (GTK_WIDGET (gcp), name, NULL); } } static gpointer gimp_context_preview_get_data (GimpContextPreview *gcp, gpointer data) { g_return_val_if_fail (GIMP_IS_CONTEXT_PREVIEW (gcp), NULL); return gcp->data; } static gint gimp_context_preview_button_press_event (GtkWidget *widget, GdkEventButton *bevent) { if (bevent->button == 1) { if (GIMP_CONTEXT_PREVIEW (widget)->show_popup) { gdk_pointer_grab (widget->window, FALSE, GDK_BUTTON_RELEASE_MASK, NULL, NULL, bevent->time); gimp_context_preview_popup_open (GIMP_CONTEXT_PREVIEW (widget), bevent->x, bevent->y); } } return FALSE; } static gint gimp_context_preview_button_release_event (GtkWidget *widget, GdkEventButton *bevent) { gboolean fast_click = TRUE; if (bevent->button == 1) { if (GIMP_CONTEXT_PREVIEW (widget)->show_popup) { gdk_pointer_ungrab (bevent->time); /* user clicked quickly if the timeout is still running */ fast_click = gcp_popup_timer; gimp_context_preview_popup_close (); } if (fast_click) gtk_signal_emit_by_name (GTK_OBJECT (widget), "clicked"); } return FALSE; } typedef struct { GimpContextPreview *gcp; gint x; gint y; } popup_timeout_args_t; static gboolean gimp_context_preview_popup_timeout (gpointer data) { popup_timeout_args_t *popup_timeout_args = data; GimpContextPreview *gcp; gint x, y; gint x_org, y_org; gint scr_w, scr_h; gcp_popup_timer = 0; g_return_val_if_fail (popup_timeout_args != NULL, FALSE); gcp = popup_timeout_args->gcp; x = popup_timeout_args->x; y = popup_timeout_args->y; g_return_val_if_fail (gcp != NULL, FALSE); if (!gcp->data) return FALSE; switch (gcp->type) { case GCP_BRUSH: { GimpBrush *brush = GIMP_BRUSH (gcp->data); gcp->popup_width = brush->mask->width; gcp->popup_height = brush->mask->height; if (GIMP_IS_BRUSH_PIPE (brush)) { GimpBrushPipe *pipe = GIMP_BRUSH_PIPE (brush); gint i; for (i = 1; i < pipe->nbrushes; i++) { brush = GIMP_BRUSH (pipe->brushes[i]); gcp->popup_width = MAX (gcp->popup_width, brush->mask->width); gcp->popup_height = MAX (gcp->popup_height, brush->mask->height); } } else if (gcp->popup_width <= gcp->width && gcp->popup_height <= gcp->height) return FALSE; } break; case GCP_PATTERN: { GimpPattern *pattern = GIMP_PATTERN (gcp->data); gcp->popup_width = pattern->mask->width; gcp->popup_height = pattern->mask->height; if (gcp->popup_width <= gcp->width && gcp->popup_height <= gcp->height) return FALSE; } break; case GCP_GRADIENT: gcp->popup_width = GRADIENT_POPUP_WIDTH; gcp->popup_height = GRADIENT_POPUP_HEIGHT; if (gcp->popup_width <= gcp->width && gcp->popup_height <= gcp->height) return FALSE; break; default: return FALSE; } /* make sure the popup exists and is not visible */ if (gcp_popup == NULL) { GtkWidget *frame; gcp_popup = gtk_window_new (GTK_WINDOW_POPUP); gtk_window_set_policy (GTK_WINDOW (gcp_popup), FALSE, FALSE, TRUE); gtk_signal_connect (GTK_OBJECT (gcp_popup), "destroy", gtk_widget_destroyed, &gcp_popup); frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT); gtk_container_add (GTK_CONTAINER (gcp_popup), frame); gtk_widget_show (frame); gcp_popup_preview = gtk_preview_new (GTK_PREVIEW_COLOR); gtk_signal_connect (GTK_OBJECT (gcp_popup_preview), "destroy", gtk_widget_destroyed, &gcp_popup_preview); gtk_container_add (GTK_CONTAINER (frame), gcp_popup_preview); gtk_widget_show (gcp_popup_preview); } else { gtk_widget_hide (gcp_popup); } /* decide where to put the popup */ gdk_window_get_origin (GTK_WIDGET (gcp)->window, &x_org, &y_org); scr_w = gdk_screen_width (); scr_h = gdk_screen_height (); x = x_org + x - (gcp->popup_width >> 1); y = y_org + y - (gcp->popup_height >> 1); x = (x < 0) ? 0 : x; y = (y < 0) ? 0 : y; x = (x + gcp->popup_width > scr_w) ? scr_w - gcp->popup_width : x; y = (y + gcp->popup_height > scr_h) ? scr_h - gcp->popup_height : y; gtk_preview_size (GTK_PREVIEW (gcp_popup_preview), gcp->popup_width, gcp->popup_height); gtk_widget_popup (gcp_popup, x, y); switch (gcp->type) { case GCP_BRUSH: gimp_context_preview_draw_brush_popup (gcp); if (GIMP_IS_BRUSH_PIPE (gcp->data) && gcp_pipe_timer == 0) { gcp_pipe_index = 0; gcp_pipe_timer = gtk_timeout_add (300, (GtkFunction) gimp_context_preview_animate_pipe, gcp); } break; case GCP_PATTERN: gimp_context_preview_draw_pattern_popup (gcp); break; case GCP_GRADIENT: gimp_context_preview_draw_gradient_popup (gcp); break; default: break; } gtk_widget_queue_draw (gcp_popup_preview); return FALSE; } static void gimp_context_preview_popup_open (GimpContextPreview *gcp, gint x, gint y) { static popup_timeout_args_t popup_timeout_args; if (gcp_popup_timer != 0) return; popup_timeout_args.gcp = gcp; popup_timeout_args.x = x; popup_timeout_args.y = y; gcp_popup_timer = gtk_timeout_add (POPUP_DELAY_MS, gimp_context_preview_popup_timeout, &popup_timeout_args); } static void gimp_context_preview_popup_close (void) { if (gcp_pipe_timer > 0) gtk_timeout_remove (gcp_pipe_timer); gcp_pipe_timer = 0; if (gcp_popup_timer) gtk_timeout_remove (gcp_popup_timer); gcp_popup_timer = 0; if (gcp_popup != NULL) gtk_widget_hide (gcp_popup); } static gboolean gimp_context_preview_data_matches_type (GimpContextPreview *gcp, gpointer data) { gboolean match = FALSE; g_return_val_if_fail (GIMP_IS_CONTEXT_PREVIEW (gcp), FALSE); switch (gcp->type) { case GCP_BRUSH: match = GIMP_IS_BRUSH (data); break; case GCP_PATTERN: case GCP_GRADIENT: match = data != NULL; /* would be nicer if these were real gtk_objects */ break; default: break; } return (match); } /* brush draw functions */ void draw_brush (GtkPreview *preview, GimpBrush *brush, gint width, gint height, gboolean is_popup) { gboolean scale = FALSE; gint brush_width, brush_height; gint offset_x, offset_y; TempBuf *mask_buf, *pixmap_buf = NULL; guchar *mask, *buf, *b; guchar bg; gint x, y; mask_buf = gimp_brush_get_mask (brush); pixmap_buf = gimp_brush_get_pixmap (brush); brush_width = mask_buf->width; brush_height = mask_buf->height; if (brush_width > width || brush_height > height) { gdouble ratio_x = (gdouble)brush_width / width; gdouble ratio_y = (gdouble)brush_height / height; brush_width = (gdouble)brush_width / MAX (ratio_x, ratio_y) + 0.5; brush_height = (gdouble)brush_height / MAX (ratio_x, ratio_y) + 0.5; mask_buf = brush_scale_mask (mask_buf, brush_width, brush_height); if (pixmap_buf) { /* TODO: the scale function should scale the pixmap and the mask in one run */ pixmap_buf = brush_scale_pixmap (pixmap_buf, brush_width, brush_height); } scale = TRUE; } offset_x = (width - brush_width) / 2; offset_y = (height - brush_height) / 2; mask = temp_buf_data (mask_buf); buf = g_new (guchar, 3 * width); memset (buf, 255, 3 * width); if (pixmap_buf) { guchar *pixmap = temp_buf_data (pixmap_buf); for (y = 0; y < offset_y; y++) gtk_preview_draw_row (preview, buf, 0, y, width); for (y = offset_y; y < brush_height + offset_y; y++) { b = buf + 3 * offset_x; for (x = 0; x < brush_width ; x++) { bg = (255 - *mask); *b++ = bg + (*mask * *pixmap++) / 255; *b++ = bg + (*mask * *pixmap++) / 255; *b++ = bg + (*mask * *pixmap++) / 255; mask++; } gtk_preview_draw_row (preview, buf, 0, y, width); } memset (buf, 255, 3 * width); for (y = brush_height + offset_y; y < height; y++) gtk_preview_draw_row (preview, buf, 0, y, width); } else { for (y = 0; y < offset_y; y++) gtk_preview_draw_row (preview, buf, 0, y, width); for (y = offset_y; y < brush_height + offset_y; y++) { b = buf + 3 * offset_x; for (x = 0; x < brush_width ; x++) { bg = 255 - *mask++; memset (b, bg, 3); b += 3; } gtk_preview_draw_row (preview, buf, 0, y, width); } memset (buf, 255, 3 * width); for (y = brush_height + offset_y; y < height; y++) gtk_preview_draw_row (preview, buf, 0, y, width); } if (scale) { offset_x = width - indicator_width; offset_y = height - indicator_height; for (y = 0; y < indicator_height; y++) (GIMP_IS_BRUSH_PIPE (brush)) ? gtk_preview_draw_row (preview, scale_pipe_indicator_bits[y][0], offset_x, offset_y + y, indicator_width) : gtk_preview_draw_row (preview, scale_indicator_bits[y][0], offset_x, offset_y + y, indicator_width); temp_buf_free (mask_buf); if (pixmap_buf) temp_buf_free (pixmap_buf); } else if (!is_popup && GIMP_IS_BRUSH_PIPE (brush)) { offset_x = width - indicator_width; offset_y = height - indicator_height; for (y = 0; y < indicator_height; y++) gtk_preview_draw_row (preview, pipe_indicator_bits[y][0], offset_x, offset_y + y, indicator_width); } g_free (buf); } static void gimp_context_preview_draw_brush_popup (GimpContextPreview *gcp) { GimpBrush *brush; g_return_if_fail (gcp != NULL && GIMP_IS_BRUSH (gcp->data)); brush = GIMP_BRUSH (gcp->data); draw_brush (GTK_PREVIEW (gcp_popup_preview), brush, gcp->popup_width, gcp->popup_height, TRUE); } static void gimp_context_preview_draw_brush (GimpContextPreview *gcp) { GimpBrush *brush; g_return_if_fail (gcp != NULL && GIMP_IS_BRUSH (gcp->data)); brush = GIMP_BRUSH (gcp->data); draw_brush (GTK_PREVIEW (gcp), brush, gcp->width, gcp->height, FALSE); } static gint gimp_context_preview_animate_pipe (GimpContextPreview *gcp) { GimpBrushPipe *pipe; GimpBrush *brush; g_return_val_if_fail (gcp != NULL && GIMP_IS_BRUSH_PIPE (gcp->data), FALSE); if (gcp_popup != NULL && !GTK_WIDGET_VISIBLE (gcp_popup)) { gcp_pipe_timer = 0; return (FALSE); } pipe = GIMP_BRUSH_PIPE (gcp->data); if (++gcp_pipe_index >= pipe->nbrushes) gcp_pipe_index = 0; brush = GIMP_BRUSH (pipe->brushes[gcp_pipe_index]); draw_brush (GTK_PREVIEW (gcp_popup_preview), brush, gcp->popup_width, gcp->popup_height, TRUE); gtk_widget_queue_draw (gcp_popup_preview); return (TRUE); } /* brush callbacks */ static gint brush_dirty_callback (GimpBrush *brush, GimpContextPreview *gcp) { gimp_context_preview_draw_brush (gcp); gtk_widget_queue_draw (GTK_WIDGET (gcp)); return TRUE; } static gint brush_rename_callback (GimpBrush *brush, GimpContextPreview *gcp) { if (gcp->show_tooltips) gimp_help_set_help_data (GTK_WIDGET (gcp), GIMP_OBJECT (brush)->name, NULL); return TRUE; } /* pattern draw functions */ void draw_pattern (GtkPreview *preview, GimpPattern *pattern, gint width, gint height) { gint pattern_width, pattern_height; guchar *mask, *src, *buf, *b; gint x, y; pattern_width = pattern->mask->width; pattern_height = pattern->mask->height; mask = temp_buf_data (pattern->mask); buf = g_new (guchar, 3 * width); if (pattern->mask->bytes == 1) { for (y = 0; y < height; y++) { b = buf; src = mask + (pattern_width * (y % pattern_height)); for (x = 0; x < width ; x++) { memset (b, src[x % pattern_width], 3); b += 3; } gtk_preview_draw_row (preview, buf, 0, y, width); } } else { for (y = 0; y < height; y++) { b = buf; src = mask + 3 * (pattern_width * (y % pattern_height)); for (x = 0; x < width ; x++) { memcpy (b, src + 3 * (x % pattern_width), 3); b += 3; } gtk_preview_draw_row (preview, buf, 0, y, width); } } g_free(buf); } static void gimp_context_preview_draw_pattern_popup (GimpContextPreview *gcp) { GimpPattern *pattern; g_return_if_fail (gcp != NULL && gcp->data != NULL); pattern = GIMP_PATTERN (gcp->data); draw_pattern (GTK_PREVIEW (gcp_popup_preview), pattern, gcp->popup_width, gcp->popup_height); } static void gimp_context_preview_draw_pattern (GimpContextPreview *gcp) { GimpPattern *pattern; g_return_if_fail (gcp != NULL && gcp->data != NULL); pattern = GIMP_PATTERN (gcp->data); draw_pattern (GTK_PREVIEW (gcp), pattern, gcp->width, gcp->height); } /* gradient draw functions */ void draw_gradient (GtkPreview *preview, gradient_t *gradient, gint width, gint height) { guchar *p0, *p1, *even, *odd; gint x, y; gdouble dx, cur_x; GimpRGB color; gdouble c0, c1; dx = 1.0 / (width - 1); cur_x = 0.0; p0 = even = g_new (guchar, 3 * width); p1 = odd = g_new (guchar, 3 * width); for (x = 0; x < width; x++) { gradient_get_color_at (gradient, cur_x, &color); if ((x / GIMP_CHECK_SIZE_SM) & 1) { c0 = GIMP_CHECK_LIGHT; c1 = GIMP_CHECK_DARK; } else { c0 = GIMP_CHECK_DARK; c1 = GIMP_CHECK_LIGHT; } *p0++ = (c0 + (color.r - c0) * color.a) * 255.0; *p0++ = (c0 + (color.g - c0) * color.a) * 255.0; *p0++ = (c0 + (color.b - c0) * color.a) * 255.0; *p1++ = (c1 + (color.r - c1) * color.a) * 255.0; *p1++ = (c1 + (color.g - c1) * color.a) * 255.0; *p1++ = (c1 + (color.b - c1) * color.a) * 255.0; cur_x += dx; } for (y = 0; y < height; y++) { if ((y / GIMP_CHECK_SIZE_SM) & 1) gtk_preview_draw_row (preview, odd, 0, y, width); else gtk_preview_draw_row (preview, even, 0, y, width); } g_free (odd); g_free (even); } static void gimp_context_preview_draw_gradient_popup (GimpContextPreview *gcp) { gradient_t *gradient; g_return_if_fail (gcp != NULL && gcp->data != NULL); gradient = (gradient_t*)(gcp->data); draw_gradient (GTK_PREVIEW (gcp_popup_preview), gradient, gcp->popup_width, gcp->popup_height); } static void gimp_context_preview_draw_gradient (GimpContextPreview *gcp) { gradient_t *gradient; g_return_if_fail (gcp != NULL && gcp->data != NULL); gradient = (gradient_t*)(gcp->data); draw_gradient (GTK_PREVIEW (gcp), gradient, gcp->width, gcp->height); }