Commit 02b111b8 authored by Owen Taylor's avatar Owen Taylor Committed by Owen Taylor

Let the user choose between elliptical, square, and diamond shaped brushes

1999-02-02  Owen Taylor  <otaylor@gtk.org>

	* app/blob.[ch] app/ink.c: Let the user choose between
	elliptical, square, and diamond shaped brushes for
	the ink tool.
parent 672cfafb
1999-02-02 Owen Taylor <otaylor@gtk.org>
* app/blob.[ch] app/ink.c: Let the user choose between
elliptical, square, and diamond shaped brushes for
the ink tool.
Tue Feb 2 22:25:57 GMT 1999 Adam D. Moss <adam@gimp.org>
* app/fileops.c: GIMP now automagically saves
......
......@@ -53,8 +53,8 @@ static void
blob_fill (Blob *b, EdgeType *present)
{
int start;
int y, x1, x2, y1, y2, i1, i2;
int i, j;
int x1, x2, i1, i2;
int i;
/* Mark empty lines at top and bottom as unused */
......@@ -205,8 +205,8 @@ blob_fill (Blob *b, EdgeType *present)
static void
blob_make_convex (Blob *b, EdgeType *present)
{
int y, x1, x2, y1, y2, i1, i2;
int i, j;
int x1, x2, y1, y2, i1, i2;
int i;
int start;
/* Walk through edges, deleting points that aren't on convex hull */
......@@ -301,9 +301,8 @@ Blob *
blob_convex_union (Blob *b1, Blob *b2)
{
Blob *result;
int y, x1, x2, y1, y2, i1, i2;
int y;
int i, j;
int start;
EdgeType *present;
/* Create the storage for the result */
......@@ -454,11 +453,148 @@ blob_line (Blob *b, int x0, int y0, int x1, int y1)
#define ELLIPSE_SHIFT 2
#define TABLE_SHIFT 14
#define TOTAL_SHIFT ELLIPSE_SHIFT + TABLE_SHIFT
#define TOTAL_SHIFT (ELLIPSE_SHIFT + TABLE_SHIFT)
static int trig_initialized = 0;
static int trig_table[TABLE_SIZE];
/* Return blob for the given (convex) polygon
*/
Blob *
blob_polygon (BlobPoint *points, int npoints)
{
int i;
int im1;
int ip1;
int ymin, ymax;
Blob *result;
EdgeType *present;
ymax = points[0].y;
ymin = points[0].y;
for (i=1; i < npoints; i++)
{
if (points[i].y > ymax)
ymax = points[i].y;
if (points[i].y < ymin)
ymin = points[i].y;
}
result = blob_new (ymin, ymax - ymin + 1);
present = g_new0 (EdgeType, result->height);
im1 = npoints - 1;
i = 0;
ip1 = 1;
for (; i < npoints ; i++)
{
int sides = 0;
int j = points[i].y - ymin;
if (points[i].y < points[im1].y)
sides |= EDGE_RIGHT;
else if (points[i].y > points[im1].y)
sides |= EDGE_LEFT;
if (points[ip1].y < points[i].y)
sides |= EDGE_RIGHT;
else if (points[ip1].y > points[i].y)
sides |= EDGE_LEFT;
if (sides & EDGE_RIGHT)
{
if (present[j] & EDGE_RIGHT)
{
result->data[j].right = MAX (result->data[j].right, points[i].x);
}
else
{
present[j] |= EDGE_RIGHT;
result->data[j].right = points[i].x;
}
}
if (sides & EDGE_LEFT)
{
if (present[j] & EDGE_LEFT)
{
result->data[j].left = MIN (result->data[j].left, points[i].x);
}
else
{
present[j] |= EDGE_LEFT;
result->data[j].left = points[i].x;
}
}
im1 = i;
ip1++;
if (ip1 == npoints)
ip1 = 0;
}
blob_fill (result, present);
g_free (present);
return result;
}
/* Scan convert a square specified by _offsets_ of major and
minor axes, and by center into a blob */
Blob *
blob_square (double xc, double yc, double xp, double yp, double xq, double yq)
{
BlobPoint points[4];
/* Make sure we order points ccw */
if (xp * yq - yq * xp < 0)
{
xq = -xq;
yq = -yq;
}
points[0].x = xc + xp + xq;
points[0].y = yc + yp + yq;
points[1].x = xc + xp - xq;
points[1].y = yc + yp - yq;
points[2].x = xc - xp - xq;
points[2].y = yc - yp - yq;
points[3].x = xc - xp + xq;
points[3].y = yc - yp + yq;
return blob_polygon (points, 4);
}
/* Scan convert a diamond specified by _offsets_ of major and
minor axes, and by center into a blob */
Blob *
blob_diamond (double xc, double yc, double xp, double yp, double xq, double yq)
{
BlobPoint points[4];
/* Make sure we order points ccw */
if (xp * yq - yq * xp < 0)
{
xq = -xq;
yq = -yq;
}
points[0].x = xc + xp;
points[0].y = yc + yp;
points[1].x = xc - xq;
points[1].y = yc - yq;
points[2].x = xc - xp;
points[2].y = yc - yp;
points[3].x = xc + xq;
points[3].y = yc + yq;
return blob_polygon (points, 4);
}
/* Scan convert an ellipse specified by _offsets_ of major and
minor axes, and by center into a blob */
Blob *
......
......@@ -25,9 +25,15 @@
#ifndef __BLOB_H__
#define __BLOB_H__
typedef struct _BlobPoint BlobPoint;
typedef struct _BlobSpan BlobSpan;
typedef struct _Blob Blob;
struct _BlobPoint {
int x;
int y;
};
struct _BlobSpan {
int left;
int right;
......@@ -41,6 +47,9 @@ struct _Blob {
Blob *blob_convex_union (Blob *b1, Blob *b2);
Blob *blob_polygon (BlobPoint *points, int npoints);
Blob *blob_square (double xc, double yc, double xp, double yp, double xq, double yq);
Blob *blob_diamond (double xc, double yc, double xp, double yp, double xq, double yq);
Blob *blob_ellipse (double xc, double yc, double xp, double yp, double xq, double yq);
void blob_bounds(Blob *b, int *x, int *y, int *width, int *height);
......
......@@ -43,6 +43,8 @@
/* the Ink structures */
typedef Blob *(*BlobFunc) (double, double, double, double, double, double);
typedef struct _InkTool InkTool;
struct _InkTool
{
......@@ -64,14 +66,19 @@ struct _InkOptions
double sensitivity;
double tilt_sensitivity;
double tilt_angle;
BlobFunc function;
};
typedef struct _BrushWidget BrushWidget;
struct _BrushWidget
{
GtkWidget *widget;
gboolean state;
};
/* Global variable to store brush widget */
static BrushWidget *brush_widget;
/* undo blocks variables */
static TileManager * undo_tiles = NULL;
......@@ -107,6 +114,15 @@ static void ink_cleanup (void);
static void ink_scale_update (GtkAdjustment *adjustment,
gdouble *value);
static void ink_type_update (GtkWidget *radio_button,
BlobFunc function);
static GdkPixmap *blob_pixmap (GdkColormap *colormap,
GdkVisual *visual,
BlobFunc function);
static void paint_blob (GdkDrawable *drawable,
GdkGC *gc,
Blob *blob);
/* Rendering functions */
static void ink_set_paint_area (InkTool *ink_tool,
......@@ -153,18 +169,22 @@ static InkOptions *
create_ink_options ()
{
GtkWidget *vbox;
GtkWidget *util_vbox;
GtkWidget *hbox;
GtkWidget *label;
GtkWidget *radio_button;
GtkWidget *pixmap_widget;
GtkWidget *slider;
GtkWidget *aspect_frame;
GtkWidget *darea;
GtkAdjustment *adj;
BrushWidget *brush_widget;
GdkPixmap *pixmap;
InkOptions *options;
/* the new options structure */
options = (InkOptions *) g_malloc (sizeof (InkOptions));
options = g_new (InkOptions, 1);
options->size = 3.0;
options->sensitivity = 1.0;
......@@ -172,6 +192,7 @@ create_ink_options ()
options->angle = 0.0;
options->tilt_sensitivity = 1.0;
options->tilt_angle = 0.0;
options->function = blob_ellipse;
/* the main vbox */
vbox = gtk_vbox_new (FALSE, 1);
......@@ -245,21 +266,80 @@ create_ink_options ()
(GtkSignalFunc) ink_scale_update,
&options->tilt_angle);
/* Brush type radiobuttons */
hbox = gtk_hbox_new (FALSE, 2);
gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
util_vbox = gtk_vbox_new (FALSE, 2);
gtk_box_pack_start (GTK_BOX(hbox), util_vbox, FALSE, FALSE, 5);
label = gtk_label_new (_("Type:"));
gtk_box_pack_start (GTK_BOX (util_vbox), label, FALSE, FALSE, 0);
pixmap = blob_pixmap (gtk_widget_get_colormap (util_vbox),
gtk_widget_get_visual (util_vbox),
blob_ellipse);
pixmap_widget = gtk_pixmap_new (pixmap, NULL);
gdk_pixmap_unref (pixmap);
radio_button = gtk_radio_button_new (NULL);
gtk_signal_connect (GTK_OBJECT (radio_button), "toggled",
GTK_SIGNAL_FUNC (ink_type_update),
(gpointer)blob_ellipse);
gtk_container_add (GTK_CONTAINER (radio_button), pixmap_widget);
gtk_box_pack_start (GTK_BOX (util_vbox), radio_button, FALSE, FALSE, 0);
pixmap = blob_pixmap (gtk_widget_get_colormap (util_vbox),
gtk_widget_get_visual (util_vbox),
blob_square);
pixmap_widget = gtk_pixmap_new (pixmap, NULL);
gdk_pixmap_unref (pixmap);
radio_button = gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (radio_button));
gtk_signal_connect (GTK_OBJECT (radio_button), "toggled",
GTK_SIGNAL_FUNC (ink_type_update),
(gpointer)blob_square);
gtk_container_add (GTK_CONTAINER (radio_button), pixmap_widget);
gtk_box_pack_start (GTK_BOX (util_vbox), radio_button, FALSE, FALSE, 0);
pixmap = blob_pixmap (gtk_widget_get_colormap (util_vbox),
gtk_widget_get_visual (util_vbox),
blob_diamond);
pixmap_widget = gtk_pixmap_new (pixmap, NULL);
gdk_pixmap_unref (pixmap);
radio_button = gtk_radio_button_new_from_widget (GTK_RADIO_BUTTON (radio_button));
gtk_signal_connect (GTK_OBJECT (radio_button), "toggled",
GTK_SIGNAL_FUNC (ink_type_update),
(gpointer)blob_diamond);
gtk_container_add (GTK_CONTAINER (radio_button), pixmap_widget);
gtk_box_pack_start (GTK_BOX (util_vbox), radio_button, FALSE, FALSE, 0);
/* Brush shape widget */
brush_widget = g_new (BrushWidget, 1);
brush_widget->state = FALSE;
hbox = gtk_hbox_new (FALSE, 2);
gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
util_vbox = gtk_vbox_new (FALSE, 2);
gtk_box_pack_start (GTK_BOX(hbox), util_vbox, FALSE, FALSE, 5);
label = gtk_label_new (_("Shape:"));
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (util_vbox), label, FALSE, FALSE, 0);
aspect_frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, 1.0, FALSE);
gtk_frame_set_shadow_type (GTK_FRAME(aspect_frame), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (hbox), aspect_frame, TRUE, TRUE, 2);
gtk_box_pack_start (GTK_BOX (util_vbox), aspect_frame, TRUE, TRUE, 2);
darea = gtk_drawing_area_new();
brush_widget->widget = darea;
gtk_drawing_area_size (GTK_DRAWING_AREA(darea), 60, 60);
gtk_container_add (GTK_CONTAINER(aspect_frame), darea);
......@@ -320,24 +400,15 @@ static void
brush_widget_draw_brush (BrushWidget *brush_widget, GtkWidget *w,
double xc, double yc, double radius)
{
int i;
Blob *b;
b = blob_ellipse(xc,yc,
radius*cos(ink_options->angle),
radius*sin(ink_options->angle),
-(radius/ink_options->aspect)*sin(ink_options->angle),
(radius/ink_options->aspect)*cos(ink_options->angle));
b = ink_options->function (xc,yc,
radius*cos(ink_options->angle),
radius*sin(ink_options->angle),
-(radius/ink_options->aspect)*sin(ink_options->angle),
(radius/ink_options->aspect)*cos(ink_options->angle));
for (i=0;i<b->height;i++)
{
if (b->data[i].left <= b->data[i].right)
gdk_draw_line (w->window,
w->style->fg_gc[w->state],
b->data[i].left,i+b->y,
b->data[i].right+1,i+b->y);
}
paint_blob (w->window, w->style->fg_gc[w->state],b);
g_free (b);
}
......@@ -431,6 +502,69 @@ ink_scale_update (GtkAdjustment *adjustment, gdouble *value)
*value = adjustment->value;
}
static void
ink_type_update (GtkWidget *radio_button,
BlobFunc function)
{
if (GTK_TOGGLE_BUTTON (radio_button)->active)
ink_options->function = function;
gtk_widget_queue_draw (brush_widget->widget);
}
/*
* Return a black-on white pixmap in the given colormap and
* visual that represents the BlobFunc 'function'
*/
static GdkPixmap *
blob_pixmap (GdkColormap *colormap,
GdkVisual *visual,
BlobFunc function)
{
GdkPixmap *pixmap;
GdkGC *black_gc, *white_gc;
GdkColor tmp_color;
Blob *blob;
pixmap = gdk_pixmap_new (NULL, 22, 21, visual->depth);
black_gc = gdk_gc_new (pixmap);
gdk_color_black (colormap, &tmp_color);
gdk_gc_set_foreground (black_gc, &tmp_color);
white_gc = gdk_gc_new (pixmap);
gdk_color_white (colormap, &tmp_color);
gdk_gc_set_foreground (white_gc, &tmp_color);
gdk_draw_rectangle (pixmap, white_gc, TRUE, 0, 0, 21, 20);
gdk_draw_rectangle (pixmap, black_gc, FALSE, 0, 0, 21, 20);
blob = (*function) (10, 10, 8, 0, 0, 8);
paint_blob (pixmap, black_gc, blob);
gdk_gc_unref (white_gc);
gdk_gc_unref (black_gc);
return pixmap;
}
/*
* Draw a blob onto a drawable with the specified graphics context
*/
static void
paint_blob (GdkDrawable *drawable, GdkGC *gc,
Blob *blob)
{
int i;
for (i=0;i<blob->height;i++)
{
if (blob->data[i].left <= blob->data[i].right)
gdk_draw_line (drawable, gc,
blob->data[i].left,i+blob->y,
blob->data[i].right+1,i+blob->y);
}
}
static Blob *
ink_pen_ellipse (gdouble x_center, gdouble y_center,
gdouble pressure, gdouble xtilt, gdouble ytilt)
......@@ -484,9 +618,9 @@ ink_pen_ellipse (gdouble x_center, gdouble y_center,
radmin = SUBSAMPLE * size/aspect;
if (radmin < 1.0) radmin = 1.0;
return blob_ellipse(x_center * SUBSAMPLE, y_center * SUBSAMPLE,
radmin*aspect*tcos, radmin*aspect*tsin,
-radmin*tsin, radmin*tcos);
return ink_options->function (x_center * SUBSAMPLE, y_center * SUBSAMPLE,
radmin*aspect*tcos, radmin*aspect*tsin,
-radmin*tsin, radmin*tcos);
}
static void
......
......@@ -53,8 +53,8 @@ static void
blob_fill (Blob *b, EdgeType *present)
{
int start;
int y, x1, x2, y1, y2, i1, i2;
int i, j;
int x1, x2, i1, i2;
int i;
/* Mark empty lines at top and bottom as unused */
......@@ -205,8 +205,8 @@ blob_fill (Blob *b, EdgeType *present)
static void
blob_make_convex (Blob *b, EdgeType *present)
{
int y, x1, x2, y1, y2, i1, i2;
int i, j;
int x1, x2, y1, y2, i1, i2;
int i;
int start;
/* Walk through edges, deleting points that aren't on convex hull */
......@@ -301,9 +301,8 @@ Blob *
blob_convex_union (Blob *b1, Blob *b2)
{
Blob *result;
int y, x1, x2, y1, y2, i1, i2;
int y;
int i, j;
int start;
EdgeType *present;
/* Create the storage for the result */
......@@ -454,11 +453,148 @@ blob_line (Blob *b, int x0, int y0, int x1, int y1)
#define ELLIPSE_SHIFT 2
#define TABLE_SHIFT 14
#define TOTAL_SHIFT ELLIPSE_SHIFT + TABLE_SHIFT
#define TOTAL_SHIFT (ELLIPSE_SHIFT + TABLE_SHIFT)
static int trig_initialized = 0;
static int trig_table[TABLE_SIZE];
/* Return blob for the given (convex) polygon
*/
Blob *
blob_polygon (BlobPoint *points, int npoints)
{
int i;
int im1;
int ip1;
int ymin, ymax;
Blob *result;
EdgeType *present;
ymax = points[0].y;
ymin = points[0].y;
for (i=1; i < npoints; i++)
{
if (points[i].y > ymax)
ymax = points[i].y;
if (points[i].y < ymin)
ymin = points[i].y;
}
result = blob_new (ymin, ymax - ymin + 1);
present = g_new0 (EdgeType, result->height);
im1 = npoints - 1;
i = 0;
ip1 = 1;
for (; i < npoints ; i++)
{
int sides = 0;
int j = points[i].y - ymin;
if (points[i].y < points[im1].y)
sides |= EDGE_RIGHT;
else if (points[i].y > points[im1].y)
sides |= EDGE_LEFT;
if (points[ip1].y < points[i].y)
sides |= EDGE_RIGHT;
else if (points[ip1].y > points[i].y)
sides |= EDGE_LEFT;
if (sides & EDGE_RIGHT)
{
if (present[j] & EDGE_RIGHT)
{
result->data[j].right = MAX (result->data[j].right, points[i].x);
}
else
{
present[j] |= EDGE_RIGHT;
result->data[j].right = points[i].x;
}
}
if (sides & EDGE_LEFT)
{
if (present[j] & EDGE_LEFT)
{
result->data[j].left = MIN (result->data[j].left, points[i].x);
}
else
{
present[j] |= EDGE_LEFT;
result->data[j].left = points[i].x;
}
}
im1 = i;
ip1++;
if (ip1 == npoints)
ip1 = 0;
}
blob_fill (result, present);
g_free (present);
return result;
}
/* Scan convert a square specified by _offsets_ of major and
minor axes, and by center into a blob */
Blob *
blob_square (double xc, double yc, double xp, double yp, double xq, double yq)
{
BlobPoint points[4];
/* Make sure we order points ccw */
if (xp * yq - yq * xp < 0)
{
xq = -xq;
yq = -yq;
}
points[0].x = xc + xp + xq;
points[0].y = yc + yp + yq;
points[1].x = xc + xp - xq;
points[1].y = yc + yp - yq;
points[2].x = xc - xp - xq;
points[2].y = yc - yp - yq;
points[3].x = xc - xp + xq;
points[3].y = yc - yp + yq;
return blob_polygon (points, 4);
}
/* Scan convert a diamond specified by _offsets_ of major and