gimpbrushpipe.c 12.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 * Copyright (C) 1999 Adrian Likins and Tor Lillqvist
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

20
21
22
#include "config.h"

#include <stdio.h>
23
#include <stdlib.h>
24
#include <string.h>
25
#include <ctype.h>
26

27
#include "apptypes.h"
28
29
30
31
32
#include "appenv.h"
#include "brush_header.h"
#include "pattern_header.h"
#include "patterns.h"
#include "gimpbrush.h"
33
34
#include "gimpbrushpipe.h"
#include "gimpbrushpipeP.h"
35
36
#include "paint_core.h"
#include "gimprc.h"
37

38
#include "libgimp/gimpintl.h"
39
#include "libgimp/gimpmath.h"
40
#include "libgimp/parasiteio.h"
41

42
43
44
45
static GimpBrushClass* gimp_brush_class;
static GtkObjectClass* gimp_object_class;

static GimpBrush *gimp_brush_pixmap_select_brush (PaintCore *paint_core);
46
static gboolean gimp_brush_pixmap_want_null_motion (PaintCore *paint_core);
47
48
49
50
51
52
53
54

static void paint_line_pixmap_mask(GImage	   *dest,
				   GimpDrawable    *drawable,
				   GimpBrushPixmap *brush,
				   guchar	   *d,
				   int		    x,
				   int              y,
				   int              bytes,
55
56
				   int              width,
				   int              mode);
57

Sven Neumann's avatar
Sven Neumann committed
58

59
static void
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
gimp_brush_pixmap_destroy (GtkObject *object)
{
  GimpBrushPixmap *pixmap;

  g_return_if_fail (GIMP_IS_BRUSH_PIXMAP (object));

  pixmap = GIMP_BRUSH_PIXMAP (object);
  
  temp_buf_free (pixmap->pixmap_mask);

  (* GTK_OBJECT_CLASS (gimp_object_class)->destroy) (object);
}

static void
gimp_brush_pixmap_class_init (GimpBrushPixmapClass *klass)
{
  GtkObjectClass *object_class;
  GimpBrushClass *brush_class;
  
  object_class = GTK_OBJECT_CLASS (klass);
  brush_class = GIMP_BRUSH_CLASS (klass);

  gimp_brush_class = gtk_type_class (gimp_brush_get_type ());

  object_class->destroy =  gimp_brush_pixmap_destroy;
  brush_class->select_brush = gimp_brush_pixmap_select_brush;
86
  brush_class->want_null_motion = gimp_brush_pixmap_want_null_motion;
87
88
89
90
91
92
93
94
95
96
97
98
}

void
gimp_brush_pixmap_init (GimpBrushPixmap *brush)
{
  brush->pixmap_mask = NULL;
  brush->pipe = NULL;
}

GtkType
gimp_brush_pixmap_get_type (void)
{
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
  static GtkType type = 0;

  if (!type)
    {
      GtkTypeInfo info =
      {
	"GimpBrushPixmap",
	sizeof (GimpBrushPixmap),
	sizeof (GimpBrushPixmapClass),
	(GtkClassInitFunc) gimp_brush_pixmap_class_init,
	(GtkObjectInitFunc) gimp_brush_pixmap_init,
	/* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
	(GtkClassInitFunc) NULL
      };

      type = gtk_type_unique (GIMP_TYPE_BRUSH, &info);
    }

118
119
  return type;
}
120

121
122
123
static GimpBrush *
gimp_brush_pixmap_select_brush (PaintCore *paint_core)
{
124
  GimpBrushPipe *pipe;
125
  int i, brushix, ix;
126
  double angle;
127
128
129

  g_return_val_if_fail (GIMP_IS_BRUSH_PIXMAP (paint_core->brush), NULL);

130
  pipe = GIMP_BRUSH_PIXMAP (paint_core->brush)->pipe;
131

132
133
  if (pipe->nbrushes == 1)
    return GIMP_BRUSH (pipe->current);
134

135
136
137
138
139
140
  brushix = 0;
  for (i = 0; i < pipe->dimension; i++)
    {
      switch (pipe->select[i])
	{
	case PIPE_SELECT_INCREMENTAL:
141
	  ix = (pipe->index[i] + 1) % pipe->rank[i];
142
143
144
145
	  break;
	case PIPE_SELECT_ANGULAR:
	  angle = atan2 (paint_core->cury - paint_core->lasty,
			 paint_core->curx - paint_core->lastx);
146
147
148
	  /* Offset angle to be compatible with PSP tubes */
	  angle += G_PI_2;
	  /* Map it to the [0..2*G_PI) interval */
149
150
	  if (angle < 0)
	    angle += 2.*G_PI;
151
152
153
	  else if (angle > 2.*G_PI)
	    angle -= 2.*G_PI;
	  ix = RINT (angle / (2.*G_PI) * pipe->rank[i]);
154
155
156
	  break;
	case PIPE_SELECT_RANDOM:
	  /* This probably isn't the right way */
157
	  ix = rand () % pipe->rank[i];
158
159
	  break;
	case PIPE_SELECT_PRESSURE:
160
	  ix = RINT (paint_core->curpressure * (pipe->rank[i] - 1));
161
162
	  break;
	case PIPE_SELECT_TILT_X:
163
	  ix = RINT (paint_core->curxtilt / 2.0 * pipe->rank[i]) + pipe->rank[i]/2;
164
165
	  break;
	case PIPE_SELECT_TILT_Y:
166
	  ix = RINT (paint_core->curytilt / 2.0 * pipe->rank[i]) + pipe->rank[i]/2;
167
	  break;
Adam D. Moss's avatar
Adam D. Moss committed
168
169
170
171
	case PIPE_SELECT_CONSTANT:
	default:
	  ix = pipe->index[i];
	  break;
172
	}
173
174
175
      pipe->index[i] = BOUNDS (ix, 0, pipe->rank[i]-1);
      brushix += pipe->stride[i] * pipe->index[i];
      /* g_print ("ix at %d: %d, brushix: %d\n", i, ix, brushix); */
176
177
    }

178
  /* Make sure is inside bounds */
179
  brushix = BOUNDS (brushix, 0, pipe->nbrushes-1);
180

181
182
183
  pipe->current = pipe->brushes[brushix];

  return GIMP_BRUSH (pipe->current);
184
}
185

186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
static gboolean
gimp_brush_pixmap_want_null_motion (PaintCore *paint_core)
{
  GimpBrushPipe *pipe;
  int i;

  g_return_val_if_fail (GIMP_IS_BRUSH_PIXMAP (paint_core->brush), TRUE);

  pipe = GIMP_BRUSH_PIXMAP (paint_core->brush)->pipe;

  if (pipe->nbrushes == 1)
    return TRUE;

  for (i = 0; i < pipe->dimension; i++)
    if (pipe->select[i] == PIPE_SELECT_ANGULAR)
      return FALSE;

  return TRUE;
}

206
207
208
static void
gimp_brush_pipe_destroy(GtkObject *object)
{
209
210
211
212
213
214
215
216
217
  GimpBrushPipe *pipe;
  int i;

  g_return_if_fail (object != NULL);
  g_return_if_fail (GIMP_IS_BRUSH_PIPE (object));

  pipe = GIMP_BRUSH_PIPE (object);

  g_free (pipe->rank);
218
  g_free (pipe->stride);
219
220
221
222
223
224
225
226
227
228

  for (i = 1; i < pipe->nbrushes; i++)
    gimp_object_destroy (pipe->brushes[i]);

  g_free (pipe->brushes);
  g_free (pipe->select);
  g_free (pipe->index);

  if (GTK_OBJECT_CLASS (gimp_object_class)->destroy)
    (* GTK_OBJECT_CLASS (gimp_object_class)->destroy) (object);
229
230
231
232
233
234
235
}

static void
gimp_brush_pipe_class_init (GimpBrushPipeClass *klass)
{
  GtkObjectClass *object_class;
  
236
237
238
239
  object_class = GTK_OBJECT_CLASS (klass);

  gimp_object_class = gtk_type_class (GIMP_TYPE_OBJECT);
  object_class->destroy =  gimp_brush_pipe_destroy;
240
241
242
}

void
243
gimp_brush_pipe_init (GimpBrushPipe *pipe)
244
{
245
246
247
248
249
  pipe->dimension = 0;
  pipe->rank = NULL;
  pipe->nbrushes = 0;
  pipe->select = NULL;
  pipe->index = NULL;
250
251
}

252
253
GtkType
gimp_brush_pipe_get_type (void)
254
255
{
  static GtkType type=0;
256
  if (!type){
257
258
    GtkTypeInfo info={
      "GimpBrushPipe",
259
260
261
262
263
264
      sizeof (GimpBrushPipe),
      sizeof (GimpBrushPipeClass),
      (GtkClassInitFunc) gimp_brush_pipe_class_init,
      (GtkObjectInitFunc) gimp_brush_pipe_init,
     /* reserved_1 */ NULL,
     /* reserved_2 */ NULL,
265
    (GtkClassInitFunc) NULL};
266
    type = gtk_type_unique (GIMP_TYPE_BRUSH_PIXMAP, &info);
267
268
269
270
271
  }
  return type;
}

GimpBrushPipe *
272
gimp_brush_pipe_load (char *filename)
273
274
{
  GimpBrushPipe *pipe;
275
  GPattern *pattern;
276
  PixPipeParams params;
277
  FILE *fp;
278
279
  guchar buf[1024];
  guchar *name;
280
  int i;
281
  int num_of_brushes;
282
  int totalcells;
283
  gchar *paramstring;
284

285
286
  if ((fp = fopen (filename, "rb")) == NULL)
    return NULL;
287

288
289
290
291
292
293
294
295
296
  /* The file format starts with a painfully simple text header
   * and we use a painfully simple way to read it
   */
  if (fgets (buf, 1024, fp) == NULL)
    {
      fclose (fp);
      return NULL;
    }
  buf[strlen (buf) - 1] = 0;
297

298
299
  pipe = GIMP_BRUSH_PIPE (gimp_type_new (GIMP_TYPE_BRUSH_PIPE));
  name = g_strdup (buf);
300
301

  /* get the number of brushes */
302
303
304
305
306
307
  if (fgets (buf, 1024, fp) == NULL)
    {
      fclose (fp);
      gimp_object_destroy (pipe);
      return NULL;
    }
308
  num_of_brushes = strtol(buf, &paramstring, 10);
309
310
311
312
313
314
315
  if (num_of_brushes < 1)
    {
      g_message (_("pixmap brush pipe should have at least one brush"));
      fclose (fp);
      gimp_object_destroy (pipe);
      return NULL;
    }
316

317
318
  while (*paramstring && isspace(*paramstring))
    paramstring++;
319

320
  if (*paramstring)
321
    {
322
323
324
      pixpipeparams_init (&params);
      pixpipeparams_parse (paramstring, &params);
      pipe->dimension = params.dim;
325
326
327
      pipe->rank = g_new (int, pipe->dimension);
      pipe->select = g_new (PipeSelectModes, pipe->dimension);
      pipe->index = g_new (int, pipe->dimension);
Sven Neumann's avatar
Sven Neumann committed
328
329
330
      /* placement is not used at all ?? */
      if (params.free_placement_string)
	g_free (params.placement);
331
332
      for (i = 0; i < pipe->dimension; i++)
	{
333
334
	  pipe->rank[i] = params.rank[i];
	  if (strcmp (params.selection[i], "incremental") == 0)
335
	    pipe->select[i] = PIPE_SELECT_INCREMENTAL;
336
	  else if (strcmp (params.selection[i], "angular") == 0)
337
	    pipe->select[i] = PIPE_SELECT_ANGULAR;
338
	  else if (strcmp (params.selection[i], "velocity") == 0)
339
	    pipe->select[i] = PIPE_SELECT_VELOCITY;
340
	  else if (strcmp (params.selection[i], "random") == 0)
341
	    pipe->select[i] = PIPE_SELECT_RANDOM;
342
	  else if (strcmp (params.selection[i], "pressure") == 0)
343
	    pipe->select[i] = PIPE_SELECT_PRESSURE;
344
	  else if (strcmp (params.selection[i], "xtilt") == 0)
345
	    pipe->select[i] = PIPE_SELECT_TILT_X;
346
	  else if (strcmp (params.selection[i], "ytilt") == 0)
347
348
349
	    pipe->select[i] = PIPE_SELECT_TILT_Y;
	  else
	    pipe->select[i] = PIPE_SELECT_CONSTANT;
Sven Neumann's avatar
Sven Neumann committed
350
351
	  if (params.free_selection_string)
	    g_free (params.selection[i]);
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
	  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);
378

Sven Neumann's avatar
Sven Neumann committed
379
  pattern = (GPattern*) g_malloc0 (sizeof (GPattern));
380

381
382
  pipe->brushes =
    (GimpBrushPixmap **) g_new0 (GimpBrushPixmap *, num_of_brushes);
383

384
385
  /* First pixmap brush in the list is the pipe itself */
  pipe->brushes[0] = GIMP_BRUSH_PIXMAP (pipe);
386

387
388
389
390
391
392
  /* Current pixmap brush is the first one. */
  pipe->current = pipe->brushes[0];

  while (pipe->nbrushes < num_of_brushes)
    {
      if (pipe->nbrushes > 0)
393
	{
394
395
	  pipe->brushes[pipe->nbrushes] =
	    GIMP_BRUSH_PIXMAP (gimp_type_new (GIMP_TYPE_BRUSH_PIXMAP));
Sven Neumann's avatar
Sven Neumann committed
396
	  g_free (GIMP_BRUSH (pipe->brushes[pipe->nbrushes])->name);
397
	  GIMP_BRUSH (pipe->brushes[pipe->nbrushes])->name = NULL;
398
	}
399
400
      
      pipe->brushes[pipe->nbrushes]->pipe = pipe;
401

402
403
404
      /* load the brush */
      if (!gimp_brush_load_brush (GIMP_BRUSH (pipe->brushes[pipe->nbrushes]),
				  fp, filename)
405
	  || !pattern_load (pattern, fp, filename))
406
       {
407
408
	  g_message (_("failed to load one of the pixmap brushes in the pipe"));
	  fclose (fp);
Sven Neumann's avatar
Sven Neumann committed
409
	  pattern_free (pattern);
410
	  gimp_object_destroy (pipe);
411
412
	  return NULL;
       }
413
414
415
416

      if (pipe->nbrushes == 0)
	{
	  /* Replace name with the whole pipe's name */
Sven Neumann's avatar
Sven Neumann committed
417
	  g_free (GIMP_BRUSH (pipe)->name);
418
419
420
	  GIMP_BRUSH (pipe)->name = name;
	}
      pipe->brushes[pipe->nbrushes]->pixmap_mask = pattern->mask;
Sven Neumann's avatar
Sven Neumann committed
421
      g_free (pattern->name);
422
      pipe->nbrushes++;
423
    }
424
425

  /*  Clean up  */
426
427
  fclose (fp);

428
  g_free (pattern);
429
430
431
432
433
434
435
  return pipe;
}

GimpBrushPipe *
gimp_brush_pixmap_load (char *filename)
{
  GimpBrushPipe *pipe;
436
  GPattern *pattern;
437
438
439
440
441
442
  FILE *fp;

  if ((fp = fopen (filename, "rb")) == NULL)
    return NULL;

  pipe = GIMP_BRUSH_PIPE (gimp_type_new (GIMP_TYPE_BRUSH_PIPE));
443

444
445
446
447
448
449
450
451
  /* A (single) pixmap brush is a pixmap pipe brush with just one pixmap */
  pipe->dimension = 1;
  pipe->rank = g_new (int, 1);
  pipe->rank[0] = 1;
  pipe->select = g_new (PipeSelectModes, 1);
  pipe->select[0] = PIPE_SELECT_INCREMENTAL;
  pipe->index = g_new (int, 1);
  pipe->index[0] = 0;
452

453
  pattern = (GPattern *) g_malloc0 (sizeof (GPattern));
454
455
456
457
458
459
460
461
462
463
464

  pipe->brushes = (GimpBrushPixmap **) g_new (GimpBrushPixmap *, 1);

  pipe->brushes[0] = GIMP_BRUSH_PIXMAP (pipe);
  pipe->current = pipe->brushes[0];

  pipe->brushes[0]->pipe = pipe;

  /* load the brush */
  if (!gimp_brush_load_brush (GIMP_BRUSH (pipe->brushes[0]),
			      fp, filename)
465
      || !pattern_load (pattern, fp, filename))
466
467
468
    {
      g_message (_("failed to load pixmap brush"));
      fclose (fp);
Sven Neumann's avatar
Sven Neumann committed
469
      pattern_free (pattern);
470
471
472
473
474
475
476
477
478
479
      gimp_object_destroy (pipe);
      return NULL;
    }
  
  pipe->brushes[0]->pixmap_mask = pattern->mask;

  pipe->nbrushes = 1;
  /*  Clean up  */
  fclose (fp);

Sven Neumann's avatar
Sven Neumann committed
480
481
  g_free (pattern->name);
  g_free (pattern);
482
  return pipe;
483
484
485
486
487
488
489
490
491
492
}

TempBuf *
gimp_brush_pixmap_pixmap (GimpBrushPixmap *brush)
{
  g_return_val_if_fail (brush != NULL, NULL);
  g_return_val_if_fail (GIMP_IS_BRUSH_PIXMAP (brush), NULL);

  return brush->pixmap_mask;
}