diff --git a/ChangeLog b/ChangeLog index fad39e5b75..1499499e3f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +1999-08-28 Tor Lillqvist + + * app/gimpbrushpipe.c: Implement the selection of brush based on + cursor direction, pressure, tilt, or a random value. (Hmm, forgot + velocity, later.) (In addition to just incrementally stepping.) + Read the brush pipe parameters from the gih file's second line. + There is no way to tune the parameters in the GIMP, they must + currently be set when saving the gih file (in the gpb plug-in). + + * app/gimpbrushpipe.h + * app/gimpbrushpipeP.h: Move the PipeSelectModes enum to the + "private" header. Add a stride array to GimpBrushPipe to make + indexing easier. + + * plug-ins/common/gpb.c: Add selection mode fields to the dialog. + Attach the pipe parameters entered as a parasite, too. + + * docs/parasites.txt + * plug-ins/common/psp.c: Use "placement", not "spacing" (which + means another thing). + Fri Aug 27 18:08:55 PDT 1999 Manish Singh * app/color_display.h: just include parasiteF.h diff --git a/app/core/gimpbrushpipe-load.c b/app/core/gimpbrushpipe-load.c index 3ea9e6f2bc..5793e82271 100644 --- a/app/core/gimpbrushpipe-load.c +++ b/app/core/gimpbrushpipe-load.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "appenv.h" #include "brush_header.h" @@ -35,6 +36,26 @@ #include "gimprc.h" #include "libgimp/gimpintl.h" +/* Code duplicated from plug-ins/common/gpb.c... + * The struct, and code to parse/build it probably should be in libgimp. + */ + +/* Parameters related to one single gih file, collected in a struct + * just for clarity. + */ +#define MAXDIM 4 +static struct { + gint step; + gint ncells; + gint dim; + gint cols; + gint rows; + gchar *placement; + gint rank[MAXDIM]; + gchar *selection[MAXDIM]; +} gihparms; + + static GimpBrushClass* gimp_brush_class; static GtkObjectClass* gimp_object_class; @@ -107,23 +128,60 @@ gimp_brush_pixmap_get_type (void) static GimpBrush * gimp_brush_pixmap_select_brush (PaintCore *paint_core) { - GimpBrushPixmap *pixmap; + GimpBrushPipe *pipe; + int i, brushix, value; + double angle; g_return_val_if_fail (GIMP_IS_BRUSH_PIXMAP (paint_core->brush), NULL); - pixmap = GIMP_BRUSH_PIXMAP (paint_core->brush); + pipe = GIMP_BRUSH_PIXMAP (paint_core->brush)->pipe; - if (pixmap->pipe->nbrushes == 1) - return GIMP_BRUSH (pixmap->pipe->current); + if (pipe->nbrushes == 1) + return GIMP_BRUSH (pipe->current); - /* Just select the next one for now. This is the place where we - * will select the correct brush based on various parameters - * in paint_core. - */ - pixmap->pipe->index[0] = (pixmap->pipe->index[0] + 1) % pixmap->pipe->nbrushes; - pixmap->pipe->current = pixmap->pipe->brushes[pixmap->pipe->index[0]]; + brushix = 0; + for (i = 0; i < pipe->dimension; i++) + { + switch (pipe->select[i]) + { + case PIPE_SELECT_CONSTANT: + /* What constant? */ + value = 0; + break; + case PIPE_SELECT_INCREMENTAL: + value = pipe->index[i] = (pipe->index[i] + 1) % pipe->rank[i]; + break; + case PIPE_SELECT_ANGULAR: + angle = atan2 (paint_core->cury - paint_core->lasty, + paint_core->curx - paint_core->lastx); + if (angle < 0) + angle += 2.*G_PI; + value = RINT (angle / (2.*G_PI) * pipe->rank[i]); + break; + case PIPE_SELECT_RANDOM: + /* This probably isn't the right way */ + value = rand () % pipe->rank[i]; + break; + case PIPE_SELECT_PRESSURE: + value = RINT (paint_core->curpressure * (pipe->rank[i] - 1)); + break; + case PIPE_SELECT_TILT_X: + value = RINT (paint_core->curxtilt / 2.0 * pipe->rank[i]) + pipe->rank[i]/2; + break; + case PIPE_SELECT_TILT_Y: + value = RINT (paint_core->curytilt / 2.0 * pipe->rank[i]) + pipe->rank[i]/2; + break; + } + brushix += pipe->stride[i] * value; + /* g_print ("value at %d: %d, brushix: %d\n", i, value, brushix); */ + } - return GIMP_BRUSH (pixmap->pipe->current); + /* If out of bounds, just select the first brush... */ + brushix = BOUNDS (brushix, 0, pipe->nbrushes-1); + + pipe->current = pipe->brushes[brushix]; + + return GIMP_BRUSH (pipe->current); } static void @@ -138,6 +196,7 @@ gimp_brush_pipe_destroy(GtkObject *object) pipe = GIMP_BRUSH_PIPE (object); g_free (pipe->rank); + g_free (pipe->stride); for (i = 1; i < pipe->nbrushes; i++) gimp_object_destroy (pipe->brushes[i]); @@ -190,6 +249,91 @@ gimp_brush_pipe_get_type (void) return type; } +static void +init_pipe_parameters () +{ + int i; + + gihparms.step = 100; + gihparms.ncells = 1; + gihparms.dim = 1; + gihparms.cols = 1; + gihparms.rows = 1; + gihparms.placement = "constant"; + for (i = 0; i < MAXDIM; i++) + gihparms.selection[i] = "random"; + gihparms.rank[0] = 1; + for (i = 1; i < MAXDIM; i++) + gihparms.rank[i] = 0; +} + +static void +parse_brush_pipe_parameters (gchar *parameters) +{ + guchar *p, *q, *r, *s; /* Don't you love single-char identifiers? */ + gint i; + + q = parameters; + while ((p = strtok (q, " \r\n")) != NULL) + { + q = NULL; + r = strchr (p, ':'); + if (r) + *r = 0; + + if (strcmp (p, "ncells") == 0) + { + if (r) + gihparms.ncells = atoi (r + 1); + } + else if (strcmp (p, "step") == 0) + { + if (r) + gihparms.step = atoi (r + 1); + } + else if (strcmp (p, "dim") == 0) + { + if (r) + gihparms.dim = atoi (r + 1); + } + else if (strcmp (p, "cols") == 0) + { + if (r) + gihparms.cols = atoi (r + 1); + } + else if (strcmp (p, "rows") == 0) + { + if (r) + gihparms.rows = atoi (r + 1); + } + else if (strcmp (p, "placement") == 0) + { + if (r) + gihparms.placement = g_strdup (r + 1); + } + else if (strncmp (p, "rank", strlen ("rank")) == 0) + { + if (r) + { + i = atoi (p + strlen ("rank")); + if (i >= 0 && i < gihparms.dim) + gihparms.rank[i] = atoi (r + 1); + } + } + else if (strncmp (p, "sel", strlen ("sel")) == 0) + { + if (r) + { + i = atoi (p + strlen ("sel")); + if (i >= 0 && i < gihparms.dim) + gihparms.selection[i] = g_strdup (r + 1); + } + } + if (r) + *r = ':'; + } +} + GimpBrushPipe * gimp_brush_pipe_load (char *filename) { @@ -198,8 +342,10 @@ gimp_brush_pipe_load (char *filename) FILE *fp; guchar buf[1024]; guchar *name; + int i; int num_of_brushes; - guchar *params; + int totalcells; + gchar *params; if ((fp = fopen (filename, "rb")) == NULL) return NULL; @@ -233,16 +379,62 @@ gimp_brush_pipe_load (char *filename) return NULL; } - /* Here we should parse the params to get the dimension, ranks, - * placement options, etc. But just use defaults for now. - */ - pipe->dimension = 1; - pipe->rank = g_new (int, 1); - pipe->rank[0] = num_of_brushes; - pipe->select = g_new (PipeSelectModes, 1); - pipe->select[0] = PIPE_SELECT_INCREMENTAL; - pipe->index = g_new (int, 1); - pipe->index[0] = 0; + while (*params && isspace(*params)) + params++; + + if (*params) + { + init_pipe_parameters (); + parse_brush_pipe_parameters (params); + pipe->dimension = gihparms.dim; + pipe->rank = g_new (int, pipe->dimension); + pipe->select = g_new (PipeSelectModes, pipe->dimension); + pipe->index = g_new (int, pipe->dimension); + for (i = 0; i < pipe->dimension; i++) + { + pipe->rank[i] = gihparms.rank[i]; + if (strcmp (gihparms.selection[i], "incremental") == 0) + pipe->select[i] = PIPE_SELECT_INCREMENTAL; + else if (strcmp (gihparms.selection[i], "angular") == 0) + pipe->select[i] = PIPE_SELECT_ANGULAR; + else if (strcmp (gihparms.selection[i], "velocity") == 0) + pipe->select[i] = PIPE_SELECT_VELOCITY; + else if (strcmp (gihparms.selection[i], "random") == 0) + pipe->select[i] = PIPE_SELECT_RANDOM; + else if (strcmp (gihparms.selection[i], "pressure") == 0) + pipe->select[i] = PIPE_SELECT_PRESSURE; + else if (strcmp (gihparms.selection[i], "xtilt") == 0) + pipe->select[i] = PIPE_SELECT_TILT_X; + else if (strcmp (gihparms.selection[i], "ytilt") == 0) + pipe->select[i] = PIPE_SELECT_TILT_Y; + else + pipe->select[i] = PIPE_SELECT_CONSTANT; + pipe->index[i] = 0; + } + } + else + { + pipe->dimension = 1; + pipe->rank = g_new (int, 1); + pipe->rank[0] = num_of_brushes; + pipe->select = g_new (PipeSelectModes, 1); + pipe->select[0] = PIPE_SELECT_INCREMENTAL; + pipe->index = g_new (int, 1); + pipe->index[0] = 0; + } + + totalcells = 1; /* Not all necessarily present, maybe */ + for (i = 0; i < pipe->dimension; i++) + totalcells *= pipe->rank[i]; + pipe->stride = g_new (int, pipe->dimension); + for (i = 0; i < pipe->dimension; i++) + { + if (i == 0) + pipe->stride[i] = totalcells / pipe->rank[i]; + else + pipe->stride[i] = pipe->stride[i-1] / pipe->rank[i]; + } + g_assert (pipe->stride[pipe->dimension-1] == 1); pattern = (GPatternP) g_malloc (sizeof (GPattern)); pattern->filename = NULL; diff --git a/app/core/gimpbrushpipe.c b/app/core/gimpbrushpipe.c index 3ea9e6f2bc..5793e82271 100644 --- a/app/core/gimpbrushpipe.c +++ b/app/core/gimpbrushpipe.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "appenv.h" #include "brush_header.h" @@ -35,6 +36,26 @@ #include "gimprc.h" #include "libgimp/gimpintl.h" +/* Code duplicated from plug-ins/common/gpb.c... + * The struct, and code to parse/build it probably should be in libgimp. + */ + +/* Parameters related to one single gih file, collected in a struct + * just for clarity. + */ +#define MAXDIM 4 +static struct { + gint step; + gint ncells; + gint dim; + gint cols; + gint rows; + gchar *placement; + gint rank[MAXDIM]; + gchar *selection[MAXDIM]; +} gihparms; + + static GimpBrushClass* gimp_brush_class; static GtkObjectClass* gimp_object_class; @@ -107,23 +128,60 @@ gimp_brush_pixmap_get_type (void) static GimpBrush * gimp_brush_pixmap_select_brush (PaintCore *paint_core) { - GimpBrushPixmap *pixmap; + GimpBrushPipe *pipe; + int i, brushix, value; + double angle; g_return_val_if_fail (GIMP_IS_BRUSH_PIXMAP (paint_core->brush), NULL); - pixmap = GIMP_BRUSH_PIXMAP (paint_core->brush); + pipe = GIMP_BRUSH_PIXMAP (paint_core->brush)->pipe; - if (pixmap->pipe->nbrushes == 1) - return GIMP_BRUSH (pixmap->pipe->current); + if (pipe->nbrushes == 1) + return GIMP_BRUSH (pipe->current); - /* Just select the next one for now. This is the place where we - * will select the correct brush based on various parameters - * in paint_core. - */ - pixmap->pipe->index[0] = (pixmap->pipe->index[0] + 1) % pixmap->pipe->nbrushes; - pixmap->pipe->current = pixmap->pipe->brushes[pixmap->pipe->index[0]]; + brushix = 0; + for (i = 0; i < pipe->dimension; i++) + { + switch (pipe->select[i]) + { + case PIPE_SELECT_CONSTANT: + /* What constant? */ + value = 0; + break; + case PIPE_SELECT_INCREMENTAL: + value = pipe->index[i] = (pipe->index[i] + 1) % pipe->rank[i]; + break; + case PIPE_SELECT_ANGULAR: + angle = atan2 (paint_core->cury - paint_core->lasty, + paint_core->curx - paint_core->lastx); + if (angle < 0) + angle += 2.*G_PI; + value = RINT (angle / (2.*G_PI) * pipe->rank[i]); + break; + case PIPE_SELECT_RANDOM: + /* This probably isn't the right way */ + value = rand () % pipe->rank[i]; + break; + case PIPE_SELECT_PRESSURE: + value = RINT (paint_core->curpressure * (pipe->rank[i] - 1)); + break; + case PIPE_SELECT_TILT_X: + value = RINT (paint_core->curxtilt / 2.0 * pipe->rank[i]) + pipe->rank[i]/2; + break; + case PIPE_SELECT_TILT_Y: + value = RINT (paint_core->curytilt / 2.0 * pipe->rank[i]) + pipe->rank[i]/2; + break; + } + brushix += pipe->stride[i] * value; + /* g_print ("value at %d: %d, brushix: %d\n", i, value, brushix); */ + } - return GIMP_BRUSH (pixmap->pipe->current); + /* If out of bounds, just select the first brush... */ + brushix = BOUNDS (brushix, 0, pipe->nbrushes-1); + + pipe->current = pipe->brushes[brushix]; + + return GIMP_BRUSH (pipe->current); } static void @@ -138,6 +196,7 @@ gimp_brush_pipe_destroy(GtkObject *object) pipe = GIMP_BRUSH_PIPE (object); g_free (pipe->rank); + g_free (pipe->stride); for (i = 1; i < pipe->nbrushes; i++) gimp_object_destroy (pipe->brushes[i]); @@ -190,6 +249,91 @@ gimp_brush_pipe_get_type (void) return type; } +static void +init_pipe_parameters () +{ + int i; + + gihparms.step = 100; + gihparms.ncells = 1; + gihparms.dim = 1; + gihparms.cols = 1; + gihparms.rows = 1; + gihparms.placement = "constant"; + for (i = 0; i < MAXDIM; i++) + gihparms.selection[i] = "random"; + gihparms.rank[0] = 1; + for (i = 1; i < MAXDIM; i++) + gihparms.rank[i] = 0; +} + +static void +parse_brush_pipe_parameters (gchar *parameters) +{ + guchar *p, *q, *r, *s; /* Don't you love single-char identifiers? */ + gint i; + + q = parameters; + while ((p = strtok (q, " \r\n")) != NULL) + { + q = NULL; + r = strchr (p, ':'); + if (r) + *r = 0; + + if (strcmp (p, "ncells") == 0) + { + if (r) + gihparms.ncells = atoi (r + 1); + } + else if (strcmp (p, "step") == 0) + { + if (r) + gihparms.step = atoi (r + 1); + } + else if (strcmp (p, "dim") == 0) + { + if (r) + gihparms.dim = atoi (r + 1); + } + else if (strcmp (p, "cols") == 0) + { + if (r) + gihparms.cols = atoi (r + 1); + } + else if (strcmp (p, "rows") == 0) + { + if (r) + gihparms.rows = atoi (r + 1); + } + else if (strcmp (p, "placement") == 0) + { + if (r) + gihparms.placement = g_strdup (r + 1); + } + else if (strncmp (p, "rank", strlen ("rank")) == 0) + { + if (r) + { + i = atoi (p + strlen ("rank")); + if (i >= 0 && i < gihparms.dim) + gihparms.rank[i] = atoi (r + 1); + } + } + else if (strncmp (p, "sel", strlen ("sel")) == 0) + { + if (r) + { + i = atoi (p + strlen ("sel")); + if (i >= 0 && i < gihparms.dim) + gihparms.selection[i] = g_strdup (r + 1); + } + } + if (r) + *r = ':'; + } +} + GimpBrushPipe * gimp_brush_pipe_load (char *filename) { @@ -198,8 +342,10 @@ gimp_brush_pipe_load (char *filename) FILE *fp; guchar buf[1024]; guchar *name; + int i; int num_of_brushes; - guchar *params; + int totalcells; + gchar *params; if ((fp = fopen (filename, "rb")) == NULL) return NULL; @@ -233,16 +379,62 @@ gimp_brush_pipe_load (char *filename) return NULL; } - /* Here we should parse the params to get the dimension, ranks, - * placement options, etc. But just use defaults for now. - */ - pipe->dimension = 1; - pipe->rank = g_new (int, 1); - pipe->rank[0] = num_of_brushes; - pipe->select = g_new (PipeSelectModes, 1); - pipe->select[0] = PIPE_SELECT_INCREMENTAL; - pipe->index = g_new (int, 1); - pipe->index[0] = 0; + while (*params && isspace(*params)) + params++; + + if (*params) + { + init_pipe_parameters (); + parse_brush_pipe_parameters (params); + pipe->dimension = gihparms.dim; + pipe->rank = g_new (int, pipe->dimension); + pipe->select = g_new (PipeSelectModes, pipe->dimension); + pipe->index = g_new (int, pipe->dimension); + for (i = 0; i < pipe->dimension; i++) + { + pipe->rank[i] = gihparms.rank[i]; + if (strcmp (gihparms.selection[i], "incremental") == 0) + pipe->select[i] = PIPE_SELECT_INCREMENTAL; + else if (strcmp (gihparms.selection[i], "angular") == 0) + pipe->select[i] = PIPE_SELECT_ANGULAR; + else if (strcmp (gihparms.selection[i], "velocity") == 0) + pipe->select[i] = PIPE_SELECT_VELOCITY; + else if (strcmp (gihparms.selection[i], "random") == 0) + pipe->select[i] = PIPE_SELECT_RANDOM; + else if (strcmp (gihparms.selection[i], "pressure") == 0) + pipe->select[i] = PIPE_SELECT_PRESSURE; + else if (strcmp (gihparms.selection[i], "xtilt") == 0) + pipe->select[i] = PIPE_SELECT_TILT_X; + else if (strcmp (gihparms.selection[i], "ytilt") == 0) + pipe->select[i] = PIPE_SELECT_TILT_Y; + else + pipe->select[i] = PIPE_SELECT_CONSTANT; + pipe->index[i] = 0; + } + } + else + { + pipe->dimension = 1; + pipe->rank = g_new (int, 1); + pipe->rank[0] = num_of_brushes; + pipe->select = g_new (PipeSelectModes, 1); + pipe->select[0] = PIPE_SELECT_INCREMENTAL; + pipe->index = g_new (int, 1); + pipe->index[0] = 0; + } + + totalcells = 1; /* Not all necessarily present, maybe */ + for (i = 0; i < pipe->dimension; i++) + totalcells *= pipe->rank[i]; + pipe->stride = g_new (int, pipe->dimension); + for (i = 0; i < pipe->dimension; i++) + { + if (i == 0) + pipe->stride[i] = totalcells / pipe->rank[i]; + else + pipe->stride[i] = pipe->stride[i-1] / pipe->rank[i]; + } + g_assert (pipe->stride[pipe->dimension-1] == 1); pattern = (GPatternP) g_malloc (sizeof (GPattern)); pattern->filename = NULL; diff --git a/app/core/gimpbrushpipe.h b/app/core/gimpbrushpipe.h index bca9ea5d08..06e80617b9 100644 --- a/app/core/gimpbrushpipe.h +++ b/app/core/gimpbrushpipe.h @@ -25,17 +25,6 @@ #include "gimpbrush.h" #include "temp_buf.h" -typedef enum -{ - PIPE_SELECT_INCREMENTAL, - PIPE_SELECT_DIRECTION, - PIPE_SELECT_VELOCITY, - PIPE_SELECT_RANDOM, - PIPE_SELECT_PRESSURE, - PIPE_SELECT_TILT_X, - PIPE_SELECT_TILT_Y -} PipeSelectModes; - typedef struct _GimpBrushPixmap GimpBrushPixmap; typedef struct _GimpBrushPipe GimpBrushPipe; diff --git a/app/gimpbrushpipe.c b/app/gimpbrushpipe.c index 3ea9e6f2bc..5793e82271 100644 --- a/app/gimpbrushpipe.c +++ b/app/gimpbrushpipe.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "appenv.h" #include "brush_header.h" @@ -35,6 +36,26 @@ #include "gimprc.h" #include "libgimp/gimpintl.h" +/* Code duplicated from plug-ins/common/gpb.c... + * The struct, and code to parse/build it probably should be in libgimp. + */ + +/* Parameters related to one single gih file, collected in a struct + * just for clarity. + */ +#define MAXDIM 4 +static struct { + gint step; + gint ncells; + gint dim; + gint cols; + gint rows; + gchar *placement; + gint rank[MAXDIM]; + gchar *selection[MAXDIM]; +} gihparms; + + static GimpBrushClass* gimp_brush_class; static GtkObjectClass* gimp_object_class; @@ -107,23 +128,60 @@ gimp_brush_pixmap_get_type (void) static GimpBrush * gimp_brush_pixmap_select_brush (PaintCore *paint_core) { - GimpBrushPixmap *pixmap; + GimpBrushPipe *pipe; + int i, brushix, value; + double angle; g_return_val_if_fail (GIMP_IS_BRUSH_PIXMAP (paint_core->brush), NULL); - pixmap = GIMP_BRUSH_PIXMAP (paint_core->brush); + pipe = GIMP_BRUSH_PIXMAP (paint_core->brush)->pipe; - if (pixmap->pipe->nbrushes == 1) - return GIMP_BRUSH (pixmap->pipe->current); + if (pipe->nbrushes == 1) + return GIMP_BRUSH (pipe->current); - /* Just select the next one for now. This is the place where we - * will select the correct brush based on various parameters - * in paint_core. - */ - pixmap->pipe->index[0] = (pixmap->pipe->index[0] + 1) % pixmap->pipe->nbrushes; - pixmap->pipe->current = pixmap->pipe->brushes[pixmap->pipe->index[0]]; + brushix = 0; + for (i = 0; i < pipe->dimension; i++) + { + switch (pipe->select[i]) + { + case PIPE_SELECT_CONSTANT: + /* What constant? */ + value = 0; + break; + case PIPE_SELECT_INCREMENTAL: + value = pipe->index[i] = (pipe->index[i] + 1) % pipe->rank[i]; + break; + case PIPE_SELECT_ANGULAR: + angle = atan2 (paint_core->cury - paint_core->lasty, + paint_core->curx - paint_core->lastx); + if (angle < 0) + angle += 2.*G_PI; + value = RINT (angle / (2.*G_PI) * pipe->rank[i]); + break; + case PIPE_SELECT_RANDOM: + /* This probably isn't the right way */ + value = rand () % pipe->rank[i]; + break; + case PIPE_SELECT_PRESSURE: + value = RINT (paint_core->curpressure * (pipe->rank[i] - 1)); + break; + case PIPE_SELECT_TILT_X: + value = RINT (paint_core->curxtilt / 2.0 * pipe->rank[i]) + pipe->rank[i]/2; + break; + case PIPE_SELECT_TILT_Y: + value = RINT (paint_core->curytilt / 2.0 * pipe->rank[i]) + pipe->rank[i]/2; + break; + } + brushix += pipe->stride[i] * value; + /* g_print ("value at %d: %d, brushix: %d\n", i, value, brushix); */ + } - return GIMP_BRUSH (pixmap->pipe->current); + /* If out of bounds, just select the first brush... */ + brushix = BOUNDS (brushix, 0, pipe->nbrushes-1); + + pipe->current = pipe->brushes[brushix]; + + return GIMP_BRUSH (pipe->current); } static void @@ -138,6 +196,7 @@ gimp_brush_pipe_destroy(GtkObject *object) pipe = GIMP_BRUSH_PIPE (object); g_free (pipe->rank); + g_free (pipe->stride); for (i = 1; i < pipe->nbrushes; i++) gimp_object_destroy (pipe->brushes[i]); @@ -190,6 +249,91 @@ gimp_brush_pipe_get_type (void) return type; } +static void +init_pipe_parameters () +{ + int i; + + gihparms.step = 100; + gihparms.ncells = 1; + gihparms.dim = 1; + gihparms.cols = 1; + gihparms.rows = 1; + gihparms.placement = "constant"; + for (i = 0; i < MAXDIM; i++) + gihparms.selection[i] = "random"; + gihparms.rank[0] = 1; + for (i = 1; i < MAXDIM; i++) + gihparms.rank[i] = 0; +} + +static void +parse_brush_pipe_parameters (gchar *parameters) +{ + guchar *p, *q, *r, *s; /* Don't you love single-char identifiers? */ + gint i; + + q = parameters; + while ((p = strtok (q, " \r\n")) != NULL) + { + q = NULL; + r = strchr (p, ':'); + if (r) + *r = 0; + + if (strcmp (p, "ncells") == 0) + { + if (r) + gihparms.ncells = atoi (r + 1); + } + else if (strcmp (p, "step") == 0) + { + if (r) + gihparms.step = atoi (r + 1); + } + else if (strcmp (p, "dim") == 0) + { + if (r) + gihparms.dim = atoi (r + 1); + } + else if (strcmp (p, "cols") == 0) + { + if (r) + gihparms.cols = atoi (r + 1); + } + else if (strcmp (p, "rows") == 0) + { + if (r) + gihparms.rows = atoi (r + 1); + } + else if (strcmp (p, "placement") == 0) + { + if (r) + gihparms.placement = g_strdup (r + 1); + } + else if (strncmp (p, "rank", strlen ("rank")) == 0) + { + if (r) + { + i = atoi (p + strlen ("rank")); + if (i >= 0 && i < gihparms.dim) + gihparms.rank[i] = atoi (r + 1); + } + } + else if (strncmp (p, "sel", strlen ("sel")) == 0) + { + if (r) + { + i = atoi (p + strlen ("sel")); + if (i >= 0 && i < gihparms.dim) + gihparms.selection[i] = g_strdup (r + 1); + } + } + if (r) + *r = ':'; + } +} + GimpBrushPipe * gimp_brush_pipe_load (char *filename) { @@ -198,8 +342,10 @@ gimp_brush_pipe_load (char *filename) FILE *fp; guchar buf[1024]; guchar *name; + int i; int num_of_brushes; - guchar *params; + int totalcells; + gchar *params; if ((fp = fopen (filename, "rb")) == NULL) return NULL; @@ -233,16 +379,62 @@ gimp_brush_pipe_load (char *filename) return NULL; } - /* Here we should parse the params to get the dimension, ranks, - * placement options, etc. But just use defaults for now. - */ - pipe->dimension = 1; - pipe->rank = g_new (int, 1); - pipe->rank[0] = num_of_brushes; - pipe->select = g_new (PipeSelectModes, 1); - pipe->select[0] = PIPE_SELECT_INCREMENTAL; - pipe->index = g_new (int, 1); - pipe->index[0] = 0; + while (*params && isspace(*params)) + params++; + + if (*params) + { + init_pipe_parameters (); + parse_brush_pipe_parameters (params); + pipe->dimension = gihparms.dim; + pipe->rank = g_new (int, pipe->dimension); + pipe->select = g_new (PipeSelectModes, pipe->dimension); + pipe->index = g_new (int, pipe->dimension); + for (i = 0; i < pipe->dimension; i++) + { + pipe->rank[i] = gihparms.rank[i]; + if (strcmp (gihparms.selection[i], "incremental") == 0) + pipe->select[i] = PIPE_SELECT_INCREMENTAL; + else if (strcmp (gihparms.selection[i], "angular") == 0) + pipe->select[i] = PIPE_SELECT_ANGULAR; + else if (strcmp (gihparms.selection[i], "velocity") == 0) + pipe->select[i] = PIPE_SELECT_VELOCITY; + else if (strcmp (gihparms.selection[i], "random") == 0) + pipe->select[i] = PIPE_SELECT_RANDOM; + else if (strcmp (gihparms.selection[i], "pressure") == 0) + pipe->select[i] = PIPE_SELECT_PRESSURE; + else if (strcmp (gihparms.selection[i], "xtilt") == 0) + pipe->select[i] = PIPE_SELECT_TILT_X; + else if (strcmp (gihparms.selection[i], "ytilt") == 0) + pipe->select[i] = PIPE_SELECT_TILT_Y; + else + pipe->select[i] = PIPE_SELECT_CONSTANT; + pipe->index[i] = 0; + } + } + else + { + pipe->dimension = 1; + pipe->rank = g_new (int, 1); + pipe->rank[0] = num_of_brushes; + pipe->select = g_new (PipeSelectModes, 1); + pipe->select[0] = PIPE_SELECT_INCREMENTAL; + pipe->index = g_new (int, 1); + pipe->index[0] = 0; + } + + totalcells = 1; /* Not all necessarily present, maybe */ + for (i = 0; i < pipe->dimension; i++) + totalcells *= pipe->rank[i]; + pipe->stride = g_new (int, pipe->dimension); + for (i = 0; i < pipe->dimension; i++) + { + if (i == 0) + pipe->stride[i] = totalcells / pipe->rank[i]; + else + pipe->stride[i] = pipe->stride[i-1] / pipe->rank[i]; + } + g_assert (pipe->stride[pipe->dimension-1] == 1); pattern = (GPatternP) g_malloc (sizeof (GPattern)); pattern->filename = NULL; diff --git a/app/gimpbrushpipe.h b/app/gimpbrushpipe.h index bca9ea5d08..06e80617b9 100644 --- a/app/gimpbrushpipe.h +++ b/app/gimpbrushpipe.h @@ -25,17 +25,6 @@ #include "gimpbrush.h" #include "temp_buf.h" -typedef enum -{ - PIPE_SELECT_INCREMENTAL, - PIPE_SELECT_DIRECTION, - PIPE_SELECT_VELOCITY, - PIPE_SELECT_RANDOM, - PIPE_SELECT_PRESSURE, - PIPE_SELECT_TILT_X, - PIPE_SELECT_TILT_Y -} PipeSelectModes; - typedef struct _GimpBrushPixmap GimpBrushPixmap; typedef struct _GimpBrushPipe GimpBrushPipe; diff --git a/app/gimpbrushpipeP.h b/app/gimpbrushpipeP.h index 84cec00238..99eb05ff0a 100644 --- a/app/gimpbrushpipeP.h +++ b/app/gimpbrushpipeP.h @@ -20,6 +20,18 @@ #ifndef __GIMPBRUSHPIPEP_H__ #define __GIMPBRUSHPIPEP_H__ +typedef enum +{ + PIPE_SELECT_CONSTANT, + PIPE_SELECT_INCREMENTAL, + PIPE_SELECT_ANGULAR, + PIPE_SELECT_VELOCITY, + PIPE_SELECT_RANDOM, + PIPE_SELECT_PRESSURE, + PIPE_SELECT_TILT_X, + PIPE_SELECT_TILT_Y +} PipeSelectModes; + /* A GimpBrushPixmap always exists as part in one and only one GimpBrushPipe * It contains a back-pointer to the GimpBrushPipe so that we can select * the next brush in the pipe with just a reference to the GimpBrushPipe. @@ -38,8 +50,10 @@ struct _GimpBrushPipe GimpBrushPixmap *current; /* Currently selected brush */ int dimension; int *rank; /* Size in each dimension */ + int *stride; /* Aux for indexing */ int nbrushes; /* Might be less than the product of the - * ranks in some special case */ + * ranks in some odd special case + */ GimpBrushPixmap **brushes; PipeSelectModes *select; /* One mode per dimension */ int *index; /* Current index for incremental dimensions */ diff --git a/app/makefile.cygwin b/app/makefile.cygwin index 47900f02f7..d848683bfd 100644 --- a/app/makefile.cygwin +++ b/app/makefile.cygwin @@ -261,7 +261,7 @@ gimp.exe : ../config.h $(gimp_OBJECTS) libgimpim.a gimp.def gimpres.o $(DLLTOOL) --base-file gimp.base --input-def gimp.def --output-exp gimp.exp $(CC) $(CFLAGS) -Wl,--base-file,gimp.base,gimp.exp -mwindows -o gimp.exe $(gimp_OBJECTS) -L . -lgimpim -L ../libgimp -lgimpi -L $(GTK)/gtk -lgtk-$(GTK_VER) -L $(GTK)/gdk/win32 -lgdk-$(GTK_VER) -L $(INTL) -lgnu-intl -L $(GLIB) -lglib-$(GLIB_VER) -lgmodule-$(GLIB_VER) gimpres.o -lgdi32 -luser32 $(DLLTOOL) --base-file gimp.base --input-def gimp.def --output-exp gimp.exp - $(CC) -v $(CFLAGS) -Wl,gimp.exp -mwindows -o gimp.exe $(gimp_OBJECTS) -L. -lgimpim -L ../libgimp -lgimpi -L $(GTK)/gtk -lgtk-$(GTK_VER) -L $(GTK)/gdk/win32 -lgdk-$(GTK_VER) -L $(INTL) -lgnu-intl -L $(GLIB) -lglib-$(GLIB_VER) -lgmodule-$(GLIB_VER) gimpres.o -lgdi32 -luser32 + $(CC) $(CFLAGS) -Wl,gimp.exp -mwindows -o gimp.exe $(gimp_OBJECTS) -L. -lgimpim -L ../libgimp -lgimpi -L $(GTK)/gtk -lgtk-$(GTK_VER) -L $(GTK)/gdk/win32 -lgdk-$(GTK_VER) -L $(INTL) -lgnu-intl -L $(GLIB) -lglib-$(GLIB_VER) -lgmodule-$(GLIB_VER) gimpres.o -lgdi32 -luser32 $(DLLTOOL) --dllname gimp.exe gimp.def --output-lib libgimp.a $(gimp_OBJECTS) .SUFFIXES: .c .o .i diff --git a/docs/parasites.txt b/docs/parasites.txt index 00e3e0230d..060266468c 100644 --- a/docs/parasites.txt +++ b/docs/parasites.txt @@ -65,15 +65,15 @@ gimp-brush-pipe-parameters" (IMAGE, PERSISTENT) and three-dimensional pipes, it probably is. rank0, rank1, ...: (one for each dimension): the index range for that dimension - spacing: "default", "constant" or "random". "constant" means - use the spacing in the first brush in the pipe. - "random" means perturb that with some suitable - random number function. (Hmm, would it be overdoing it - if the pipe also could specify what random function - and its parameters...?) + placement: "default", "constant" or "random". "constant" means + use the spacing in the first brush in the pipe. + "random" means perturb that with some suitable + random number function. (Hmm, would it be overdoing it + if the pipe also could specify what random function + and its parameters...?) sel0, sel1, ...: "default", "random", "incremental", "angular", "pressure", "velocity", and whatever else suitable we might - think of ;-) How one index from each dimension is + think of ;-) Determines how one index from each dimension is selected (until we have pinpointed the brush to use). "tiff-save-options" (IMAGE) diff --git a/plug-ins/common/gpb.c b/plug-ins/common/gpb.c index 1b266c7ed1..e6122ae29d 100644 --- a/plug-ins/common/gpb.c +++ b/plug-ins/common/gpb.c @@ -59,14 +59,17 @@ static gint run_flag = 0; static gint num_layers_with_alpha; /* Parameters related to one single gih file, collected in a struct - * just for clarity. */ + * just for clarity. + */ static struct { gint step; gint ncells; gint dim; gint cols; gint rows; + gchar *placement; gint rank[MAXDIM]; + gchar *selection[MAXDIM]; } gihparms; /* Declare some local functions. @@ -171,6 +174,13 @@ entry_callback (GtkWidget *widget, } } +static void +cb_callback (GtkWidget *widget, + gpointer data) +{ + *((char **) data) = gtk_entry_get_text (GTK_ENTRY (widget)); +} + static void close_callback (GtkWidget *widget, gpointer data) @@ -315,12 +325,14 @@ static gint gih_save_dialog () { GtkWidget *dlg; - GtkWidget *table; + GtkWidget *table, *dimtable; GtkWidget *label; GtkObject *adjustment; GtkWidget *spinbutton; GtkWidget *entry; GtkWidget *box; + GtkWidget *cb; + GList *cbitems = NULL; gint i; gchar **argv; gint argc; @@ -353,6 +365,36 @@ gih_save_dialog () common_save_dialog (dlg, table); + /* + * Number of cells: ___ + */ + + gtk_table_resize (GTK_TABLE (table), + GTK_TABLE (table)->nrows + 1, GTK_TABLE (table)->ncols); + + label = gtk_label_new ("Number of cells:"); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, + GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (label); + + adjustment = gtk_adjustment_new (gihparms.ncells, 1, 1000, 1, 10, 10); + spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 1, 0); + gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton), + GTK_SHADOW_NONE); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE); + gtk_widget_set_usize (spinbutton, 75, 0); + box = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, TRUE, 0); + gtk_table_attach (GTK_TABLE (table), box, 1, 2, + GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed", + (GtkSignalFunc) adjustment_callback, &gihparms.ncells); + gtk_widget_show (spinbutton); + gtk_widget_show (box); + /* * Display as: __ rows x __ cols */ @@ -403,36 +445,6 @@ gih_save_dialog () GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (box); - /* - * Number of cells: ___ - */ - - gtk_table_resize (GTK_TABLE (table), - GTK_TABLE (table)->nrows + 1, GTK_TABLE (table)->ncols); - - label = gtk_label_new ("Number of cells:"); - gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_table_attach (GTK_TABLE (table), label, 0, 1, - GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows, - GTK_FILL, GTK_FILL, 0, 0); - gtk_widget_show (label); - - adjustment = gtk_adjustment_new (gihparms.ncells, 1, 1000, 1, 10, 10); - spinbutton = gtk_spin_button_new (GTK_ADJUSTMENT (adjustment), 1, 0); - gtk_spin_button_set_shadow_type (GTK_SPIN_BUTTON (spinbutton), - GTK_SHADOW_NONE); - gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE); - gtk_widget_set_usize (spinbutton, 75, 0); - box = gtk_hbox_new (FALSE, 0); - gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, TRUE, 0); - gtk_table_attach (GTK_TABLE (table), box, 1, 2, - GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows, - GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); - gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed", - (GtkSignalFunc) adjustment_callback, &gihparms.ncells); - gtk_widget_show (spinbutton); - gtk_widget_show (box); - /* * Dimension: ___ */ @@ -477,7 +489,7 @@ gih_save_dialog () GTK_FILL, GTK_FILL, 0, 0); gtk_widget_show (label); - box = gtk_hbox_new (FALSE, 0); + dimtable = gtk_table_new (MAXDIM, 1, FALSE); for (i = 0; i < MAXDIM; i++) { adjustment = gtk_adjustment_new (gihparms.rank[i], 0, 100, 1, 1, 1); @@ -486,15 +498,64 @@ gih_save_dialog () GTK_SHADOW_NONE); gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE); gtk_widget_set_usize (spinbutton, 75, 0); - gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, TRUE, 2); + box = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (box), spinbutton, FALSE, TRUE, 0); + gtk_table_attach (GTK_TABLE (dimtable), box, i, i + 1, 0, 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 3, 0); gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed", (GtkSignalFunc) adjustment_callback, &gihparms.rank[i]); gtk_widget_show (spinbutton); + gtk_widget_show (box); } + gtk_table_attach (GTK_TABLE (table), dimtable, 1, 2, + GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows, + GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_widget_show (dimtable); + + /* + * Selection: ______ ______ ______ ______ ______ + */ + + gtk_table_resize (GTK_TABLE (table), + GTK_TABLE (table)->nrows + 1, GTK_TABLE (table)->ncols); + + label = gtk_label_new ("Selection:"); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, + GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (label); + + cbitems = g_list_append (cbitems, "incremental"); + cbitems = g_list_append (cbitems, "angular"); + cbitems = g_list_append (cbitems, "random"); + cbitems = g_list_append (cbitems, "velocity"); + cbitems = g_list_append (cbitems, "pressure"); + cbitems = g_list_append (cbitems, "xtilt"); + cbitems = g_list_append (cbitems, "ytilt"); + + box = gtk_hbox_new (FALSE, 0); + for (i = 0; i < MAXDIM; i++) + { + cb = gtk_combo_new (); + gtk_combo_set_popdown_strings (GTK_COMBO (cb), cbitems); + if (gihparms.selection[i]) + gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (cb)->entry), gihparms.selection[i]); + gtk_entry_set_editable (GTK_ENTRY (GTK_COMBO (cb)->entry), FALSE); + + gtk_box_pack_start (GTK_BOX (box), cb, FALSE, TRUE, 2); + gtk_signal_connect (GTK_OBJECT (GTK_COMBO (cb)->entry), "changed", + (GtkSignalFunc) cb_callback, &gihparms.selection[i]); + gtk_widget_show (cb); + } + + g_list_free (cbitems); + gtk_table_attach (GTK_TABLE (table), box, 1, 2, GTK_TABLE (table)->nrows - 1, GTK_TABLE (table)->nrows, GTK_SHRINK, GTK_EXPAND | GTK_FILL, 0, 0); gtk_widget_show (box); + gtk_widget_show (dlg); gtk_main (); @@ -700,6 +761,24 @@ gpb_save_image (char *filename, return TRUE; } +static void +init_pipe_parameters () +{ + int i; + + gihparms.step = 100; + gihparms.ncells = 1; + gihparms.dim = 1; + gihparms.cols = 1; + gihparms.rows = 1; + gihparms.placement = "constant"; + for (i = 0; i < MAXDIM; i++) + gihparms.selection[i] = "random"; + gihparms.rank[0] = 1; + for (i = 1; i < MAXDIM; i++) + gihparms.rank[i] = 0; +} + static void parse_brush_pipe_parameters (gchar *parameters) { @@ -741,11 +820,28 @@ parse_brush_pipe_parameters (gchar *parameters) if (r) gihparms.rows = atoi (r + 1); } - else if (strncmp (p, "rank", 4) == 0 && r) + else if (strcmp (p, "placement") == 0) { - i = atoi (p + 4); - if (i >= 0 && i < gihparms.dim) - gihparms.rank[i] = atoi (r + 1); + if (r) + gihparms.placement = g_strdup (r + 1); + } + else if (strncmp (p, "rank", strlen ("rank")) == 0 && r) + { + if (r) + { + i = atoi (p + strlen ("rank")); + if (i >= 0 && i < gihparms.dim) + gihparms.rank[i] = atoi (r + 1); + } + } + else if (strncmp (p, "sel", strlen ("sel")) == 0 && r) + { + if (r) + { + i = atoi (p + strlen ("sel")); + if (i >= 0 && i < gihparms.dim) + gihparms.selection[i] = g_strdup (r + 1); + } } if (r) *r = ':'; @@ -753,12 +849,16 @@ parse_brush_pipe_parameters (gchar *parameters) IFDBG(2) g_message ("parsed parasite: " "ncells:%d step:%d dim:%d cols:%d rows:%d " - "rank0:%d rank1:%d rank2:%d rank3:%d", + "placement:%s " + "rank0:%d rank1:%d rank2:%d rank3:%d" + "sel%d:%s sel%d:%s sel%d:%s ", gihparms.ncells, gihparms.step, - gihparms.dim, - gihparms.cols, gihparms.rows, + gihparms.dim, gihparms.cols, gihparms.rows, + gihparms.placement, gihparms.rank[0], gihparms.rank[1], - gihparms.rank[2], gihparms.rank[3]); + gihparms.rank[2], gihparms.rank[3], + gihparms.selection[0], gihparms.selection[1], + gihparms.selection[2], gihparms.selection[3]); } static gchar * @@ -769,13 +869,17 @@ build_brush_pipe_parameters () int i; - g_string_sprintf (s, "ncells:%d step:%d dim:%d cols:%d rows:%d", + g_string_sprintf (s, "ncells:%d step:%d dim:%d cols:%d rows:%d placement:%s", gihparms.ncells, gihparms.step, gihparms.dim, - gihparms.cols, gihparms.rows); + gihparms.cols, gihparms.rows, + gihparms.placement); for (i = 0; i < gihparms.dim; i++) - g_string_sprintfa (s, " rank%d:%d", i, gihparms.rank[i]); + { + g_string_sprintfa (s, " rank%d:%d", i, gihparms.rank[i]); + g_string_sprintfa (s, " sel%d:%s", i, gihparms.selection[i]); + } str = s->str; g_string_free (s, FALSE); @@ -792,6 +896,7 @@ gih_save_image (char *filename, GDrawable *drawable; GPixelRgn pixel_rgn; FILE *file; + Parasite *pipe_parasite; gchar *msg, *pars, *ncells; gint32 *layer_ID; gint nlayers, layer, row, col; @@ -826,6 +931,13 @@ gih_save_image (char *filename, g_free (ncells); return FALSE; } + + pipe_parasite = parasite_new ("gimp-brush-pipe-parameters", + PARASITE_PERSISTENT, + strlen (pars) + 1, pars); + gimp_image_attach_parasite (image_ID, pipe_parasite); + parasite_free (pipe_parasite); + g_free (pars); g_free (ncells); @@ -958,11 +1070,12 @@ run (char *name, /* Possibly retrieve data */ gimp_get_data ("file_gih_save", &info); pipe_parasite = gimp_image_find_parasite (image_ID, "gimp-brush-pipe-parameters"); + init_pipe_parameters (); if (pipe_parasite) { parse_brush_pipe_parameters (pipe_parasite->data); } - + if (!gih_save_dialog ()) return; break; diff --git a/plug-ins/common/psp.c b/plug-ins/common/psp.c index c60682c886..9303f763ba 100644 --- a/plug-ins/common/psp.c +++ b/plug-ins/common/psp.c @@ -1499,7 +1499,7 @@ read_tube_block (FILE *f, parasite_text = g_strdup_printf ("ncells:%d step:%d dim:%d cols:%d rows:%d " "rank0:%d " - "spacing:%s sel0:%s", + "placement:%s sel0:%s", cell_count, step_size, 1, column_count, row_count, cell_count, (placement_mode == tpmRandom ? "random" :