mirror of https://github.com/GNOME/gimp.git
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.
This commit is contained in:
parent
672cfafb02
commit
02b111b8ce
|
@ -1,3 +1,9 @@
|
|||
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
|
||||
|
|
150
app/blob.c
150
app/blob.c
|
@ -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);
|
||||
|
||||
|
|
184
app/ink.c
184
app/ink.c
|
@ -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
|
||||
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
|
||||
|
|
150
app/tools/blob.c
150
app/tools/blob.c
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
184
app/tools/ink.c
184
app/tools/ink.c
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue