Gimp/app/gimpcontextpreview.c
Sven Neumann 3cff8419db Jens Lautenbacher <jtl@gimp.org>
2000-12-18  Sven Neumann  <sven@gimp.org>
	    Jens Lautenbacher <jtl@gimp.org>

	* app/Makefile.am

	* app/gimpbrushlistP.h
	* app/gimpbrushpipeP.h
	* app/gimpobjectP.h: removed these three files

	* app/parasitelistP.h
	* app/channels_dialog.c
	* app/docindex.c
	* app/gimpdrawable.c
	* app/gimpdrawableP.h
	* app/gimpimage.c
	* app/gimpimageP.h
	* app/gimplist.[ch]
	* app/gimpobject.c
	* app/gimpobject.h
	* app/gimpsetP.h: changed according to header removal

	* app/airbrush.c
	* app/brush_select.[ch]
	* app/brushes_cmds.c
	* app/gimpbrush.[ch]
	* app/gimpbrushgenerated.[ch]
	* app/gimpbrushlist.[ch]
	* app/gimpbrushpipe.[ch]
	* app/gimpcontextpreview.c
	* app/paint_core.c
	* app/paintbrush.c
	* app/pencil.c
	* tools/pdbgen/pdb/brushes.pdb: Big Brushes Cleanup.

	The GimpBrush* object hierarchy and the file formats were broken by
	"design". This made it overly difficult to read and write pixmap
	brushes and brush pipes, leading to the situation that The GIMP was
	not able to read it's very own file formats. Since the GimpBrush
	format did support arbitrary color depths, the introduction of a
	file format for pixmap brushes was unnecessary.

	The GimpBrushPixmap object is dead. GimpBrush has an additional
	pixmap temp_buf and handles pixmap brushes transparently. The file
	format of pixmap brushes is not any longer a grayscale brush plus
	a pattern, but a simple brush with RGBA data. The old brushes can
	still be loaded, but the .gpb format is deprecated.

	GimpBrushPipe derives from GimpBrush. The fileformat is still a text
	header, followed by a number of brushes, but those brushes are stored
	in the new GimpBrush format (no pattern anymore). The pipe does not
	care about the depth of the contained GimpBrushes, so we get
	grayscale BrushPipes for free. Since the brush loader still loads the
	old format, old .gih files can also still be loaded.

	Since the brushes in the GimpBrushPipe do not any longer contain a
	pointer to the pipe object, we do only temporarily switch brushes
	in the paint_core routines. This is not very elegant, but the best
	we can do without a major redesign.

	* app/patterns.[ch]: changed the loader to work with a filedescriptor
	instead of a filehandle to make it work with the new brush loading
	code.

	* plug-ins/common/.cvsignore
	* plug-ins/common/Makefile.am
	* plug-ins/common/plugin-defs.pl
	* plug-ins/common/gih.c: new plug-in that saves GIH files in the
	new format (loader will follow soon)

	* plug-ins/common/gpb.c: removed since Pixmap Brushes are no longer
	supported as a special file format.

	* plug-ins/common/gbr.c: load and save brushes in the new brush format
	which allows RGBA brushes too.

	* plug-ins/common/pat.c: load and save grayscale patterns too
2000-12-18 15:14:08 +00:00

973 lines
25 KiB
C

/* 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 <string.h>
#include <gtk/gtk.h>
#include "apptypes.h"
#include "appenv.h"
#include "brush_scale.h"
#include "gimpbrush.h"
#include "gimpbrushpipe.h"
#include "gimpcontextpreview.h"
#include "gimpdnd.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), "rename",
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 = brush->name;
}
break;
case GCP_PATTERN:
{
GPattern *pattern = (GPattern *) (gcp->data);
name = 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:
{
GPattern *pattern = (GPattern*)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), brush->name, NULL);
return TRUE;
}
/* pattern draw functions */
void
draw_pattern (GtkPreview *preview,
GPattern *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)
{
GPattern *pattern;
g_return_if_fail (gcp != NULL && gcp->data != NULL);
pattern = (GPattern*)(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)
{
GPattern *pattern;
g_return_if_fail (gcp != NULL && gcp->data != NULL);
pattern = (GPattern*)(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;
gdouble r, g, b, a;
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, &r, &g, &b, &a);
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 + (r - c0) * a) * 255.0;
*p0++ = (c0 + (g - c0) * a) * 255.0;
*p0++ = (c0 + (b - c0) * a) * 255.0;
*p1++ = (c1 + (r - c1) * a) * 255.0;
*p1++ = (c1 + (g - c1) * a) * 255.0;
*p1++ = (c1 + (b - c1) * 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);
}