gimpbrushpipe.c 13.1 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

Sven Neumann's avatar
Sven Neumann committed
27 28
#include <gtk/gtk.h>

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

#include "libgimp/gimpmath.h"
41
#include "libgimp/gimpparasiteio.h"
42

43 44
#include "libgimp/gimpintl.h"

45

Michael Natterer's avatar
Michael Natterer committed
46 47
static GimpBrushClass       *gimp_brush_class;
static GimpBrushPixmapClass *gimp_brush_pixmap_class;
48

Michael Natterer's avatar
Michael Natterer committed
49 50
static GimpBrush *gimp_brush_pixmap_select_brush     (PaintCore *paint_core);
static gboolean   gimp_brush_pixmap_want_null_motion (PaintCore *paint_core);
51

52
static void
53 54 55 56
gimp_brush_pixmap_destroy (GtkObject *object)
{
  GimpBrushPixmap *pixmap;

57
  g_return_if_fail (object != NULL);
58 59 60
  g_return_if_fail (GIMP_IS_BRUSH_PIXMAP (object));

  pixmap = GIMP_BRUSH_PIXMAP (object);
Michael Natterer's avatar
Michael Natterer committed
61

62 63
  if (pixmap->pixmap_mask)
    temp_buf_free (pixmap->pixmap_mask);
64

Michael Natterer's avatar
Michael Natterer committed
65
  if (GTK_OBJECT_CLASS (gimp_brush_class)->destroy)
66
    GTK_OBJECT_CLASS (gimp_brush_class)->destroy (object);
67 68 69 70 71 72 73 74 75 76 77 78 79
}

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 ());

Michael Natterer's avatar
Michael Natterer committed
80 81
  object_class->destroy         = gimp_brush_pixmap_destroy;
  brush_class->select_brush     = gimp_brush_pixmap_select_brush;
82
  brush_class->want_null_motion = gimp_brush_pixmap_want_null_motion;
83 84 85 86 87 88
}

void
gimp_brush_pixmap_init (GimpBrushPixmap *brush)
{
  brush->pixmap_mask = NULL;
89
  brush->pipe        = NULL;
90 91 92 93 94
}

GtkType
gimp_brush_pixmap_get_type (void)
{
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
  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);
    }

114 115
  return type;
}
116

117 118 119
static GimpBrush *
gimp_brush_pixmap_select_brush (PaintCore *paint_core)
{
120
  GimpBrushPipe *pipe;
121
  int i, brushix, ix;
122
  double angle;
123 124 125

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

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

128 129
  if (pipe->nbrushes == 1)
    return GIMP_BRUSH (pipe->current);
130

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

174
  /* Make sure is inside bounds */
175
  brushix = CLAMP (brushix, 0, pipe->nbrushes-1);
176

177 178 179
  pipe->current = pipe->brushes[brushix];

  return GIMP_BRUSH (pipe->current);
180
}
181

182 183 184 185
static gboolean
gimp_brush_pixmap_want_null_motion (PaintCore *paint_core)
{
  GimpBrushPipe *pipe;
186
  gint i;
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201

  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;
}

202
static void
Michael Natterer's avatar
Michael Natterer committed
203
gimp_brush_pipe_destroy (GtkObject *object)
204
{
205
  GimpBrushPipe *pipe;
Michael Natterer's avatar
Michael Natterer committed
206
  gint i;
207 208 209 210 211 212 213

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

  pipe = GIMP_BRUSH_PIPE (object);

  g_free (pipe->rank);
214
  g_free (pipe->stride);
215 216

  for (i = 1; i < pipe->nbrushes; i++)
217 218
    if (pipe->brushes[i])
      gtk_object_unref (GTK_OBJECT (pipe->brushes[i]));
219 220 221 222 223

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

Michael Natterer's avatar
Michael Natterer committed
224
  if (GTK_OBJECT_CLASS (gimp_brush_pixmap_class)->destroy)
225
    GTK_OBJECT_CLASS (gimp_brush_pixmap_class)->destroy (object);
226 227 228 229 230 231
}

static void
gimp_brush_pipe_class_init (GimpBrushPipeClass *klass)
{
  GtkObjectClass *object_class;
Michael Natterer's avatar
Michael Natterer committed
232

233 234
  object_class = GTK_OBJECT_CLASS (klass);

Michael Natterer's avatar
Michael Natterer committed
235 236
  gimp_brush_pixmap_class = gtk_type_class (GIMP_TYPE_BRUSH_PIXMAP);
  object_class->destroy = gimp_brush_pipe_destroy;
237 238 239
}

void
240
gimp_brush_pipe_init (GimpBrushPipe *pipe)
241
{
242
  pipe->current   = NULL;
243
  pipe->dimension = 0;
Michael Natterer's avatar
Michael Natterer committed
244
  pipe->rank      = NULL;
245
  pipe->stride    = NULL;
Michael Natterer's avatar
Michael Natterer committed
246
  pipe->nbrushes  = 0;
247
  pipe->brushes   = NULL;
Michael Natterer's avatar
Michael Natterer committed
248 249
  pipe->select    = NULL;
  pipe->index     = NULL;
250 251
}

252 253
GtkType
gimp_brush_pipe_get_type (void)
254
{
Michael Natterer's avatar
Michael Natterer committed
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
  static GtkType type = 0;

  if (!type)
    {
      GtkTypeInfo info =
      {
	"GimpBrushPipe",
	sizeof (GimpBrushPipe),
	sizeof (GimpBrushPipeClass),
	(GtkClassInitFunc) gimp_brush_pipe_class_init,
	(GtkObjectInitFunc) gimp_brush_pipe_init,
	/* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
	(GtkClassInitFunc) NULL
      };

      type = gtk_type_unique (GIMP_TYPE_BRUSH_PIXMAP, &info);
    }

274 275 276 277
  return type;
}

GimpBrushPipe *
Michael Natterer's avatar
Michael Natterer committed
278
gimp_brush_pipe_load (gchar *filename)
279
{
280 281 282
  GimpBrushPipe     *pipe;
  GPattern          *pattern;
  GimpPixPipeParams  params;
Michael Natterer's avatar
Michael Natterer committed
283 284
  FILE  *fp;
  gchar  buf[1024];
Sven Neumann's avatar
Sven Neumann committed
285
  gchar *name;
Michael Natterer's avatar
Michael Natterer committed
286 287 288
  gint   i;
  gint   num_of_brushes;
  gint   totalcells;
289
  gchar *paramstring;
290

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

294 295 296 297 298 299 300 301 302
  /* 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;
303

304
  pipe = GIMP_BRUSH_PIPE (gtk_type_new (GIMP_TYPE_BRUSH_PIPE));
305
  name = g_strdup (buf);
306 307

  /* get the number of brushes */
308 309 310
  if (fgets (buf, 1024, fp) == NULL)
    {
      fclose (fp);
311
      gtk_object_sink (GTK_OBJECT (pipe));
312 313
      return NULL;
    }
Michael Natterer's avatar
Michael Natterer committed
314
  num_of_brushes = strtol (buf, &paramstring, 10);
315 316
  if (num_of_brushes < 1)
    {
317
      g_message (_("Brush pipes should have at least one brush."));
318
      fclose (fp);
319
      gtk_object_sink (GTK_OBJECT (pipe));
320 321
      return NULL;
    }
322

Michael Natterer's avatar
Michael Natterer committed
323
  while (*paramstring && isspace (*paramstring))
324
    paramstring++;
325

326
  if (*paramstring)
327
    {
328 329
      gimp_pixpipe_params_init (&params);
      gimp_pixpipe_params_parse (paramstring, &params);
330

331
      pipe->dimension = params.dim;
332 333 334 335
      pipe->rank      = g_new0 (gint, pipe->dimension);
      pipe->select    = g_new0 (PipeSelectModes, pipe->dimension);
      pipe->index     = g_new0 (gint, pipe->dimension);

Sven Neumann's avatar
Sven Neumann committed
336 337 338
      /* placement is not used at all ?? */
      if (params.free_placement_string)
	g_free (params.placement);
339

340 341
      for (i = 0; i < pipe->dimension; i++)
	{
342 343
	  pipe->rank[i] = params.rank[i];
	  if (strcmp (params.selection[i], "incremental") == 0)
344
	    pipe->select[i] = PIPE_SELECT_INCREMENTAL;
345
	  else if (strcmp (params.selection[i], "angular") == 0)
346
	    pipe->select[i] = PIPE_SELECT_ANGULAR;
347
	  else if (strcmp (params.selection[i], "velocity") == 0)
348
	    pipe->select[i] = PIPE_SELECT_VELOCITY;
349
	  else if (strcmp (params.selection[i], "random") == 0)
350
	    pipe->select[i] = PIPE_SELECT_RANDOM;
351
	  else if (strcmp (params.selection[i], "pressure") == 0)
352
	    pipe->select[i] = PIPE_SELECT_PRESSURE;
353
	  else if (strcmp (params.selection[i], "xtilt") == 0)
354
	    pipe->select[i] = PIPE_SELECT_TILT_X;
355
	  else if (strcmp (params.selection[i], "ytilt") == 0)
356 357 358
	    pipe->select[i] = PIPE_SELECT_TILT_Y;
	  else
	    pipe->select[i] = PIPE_SELECT_CONSTANT;
Sven Neumann's avatar
Sven Neumann committed
359 360
	  if (params.free_selection_string)
	    g_free (params.selection[i]);
361 362 363 364 365 366
	  pipe->index[i] = 0;
	}
    }
  else
    {
      pipe->dimension = 1;
Michael Natterer's avatar
Michael Natterer committed
367 368 369
      pipe->rank      = g_new (gint, 1);
      pipe->rank[0]   = num_of_brushes;
      pipe->select    = g_new (PipeSelectModes, 1);
370
      pipe->select[0] = PIPE_SELECT_INCREMENTAL;
Michael Natterer's avatar
Michael Natterer committed
371 372
      pipe->index     = g_new (gint, 1);
      pipe->index[0]  = 0;
373 374 375 376 377
    }

  totalcells = 1;		/* Not all necessarily present, maybe */
  for (i = 0; i < pipe->dimension; i++)
    totalcells *= pipe->rank[i];
378
  pipe->stride = g_new0 (gint, pipe->dimension);
379 380 381 382 383 384 385 386
  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);
387

Michael Natterer's avatar
Michael Natterer committed
388
  pattern = g_new0 (GPattern, 1);
389

Michael Natterer's avatar
Michael Natterer committed
390
  pipe->brushes = g_new0 (GimpBrushPixmap *, num_of_brushes);
391

392 393
  /* First pixmap brush in the list is the pipe itself */
  pipe->brushes[0] = GIMP_BRUSH_PIXMAP (pipe);
394

395 396 397 398 399 400
  /* Current pixmap brush is the first one. */
  pipe->current = pipe->brushes[0];

  while (pipe->nbrushes < num_of_brushes)
    {
      if (pipe->nbrushes > 0)
401
	{
402
	  pipe->brushes[pipe->nbrushes] =
403
	    GIMP_BRUSH_PIXMAP (gtk_type_new (GIMP_TYPE_BRUSH_PIXMAP));
404 405 406 407

	  gtk_object_ref (GTK_OBJECT (pipe->brushes[pipe->nbrushes]));
	  gtk_object_sink (GTK_OBJECT (pipe->brushes[pipe->nbrushes]));

Sven Neumann's avatar
Sven Neumann committed
408
	  g_free (GIMP_BRUSH (pipe->brushes[pipe->nbrushes])->name);
409
	  GIMP_BRUSH (pipe->brushes[pipe->nbrushes])->name = NULL;
410
	}
Michael Natterer's avatar
Michael Natterer committed
411

412
      pipe->brushes[pipe->nbrushes]->pipe = pipe;
413

414 415
      /* load the brush */
      if (!gimp_brush_load_brush (GIMP_BRUSH (pipe->brushes[pipe->nbrushes]),
416 417 418
				  fp, filename))
	{
	  g_message (_("Failed to load one of the brushes in the brush pipe."));
Sven Neumann's avatar
Sven Neumann committed
419
	  pattern_free (pattern);
420
	  gtk_object_sink (GTK_OBJECT (pipe));
421
	  return NULL;
422 423 424 425 426 427 428 429
	}

      if (!pattern_load (pattern, fp, filename))
	{
	  g_message (_("Failed to load one of the brushes in the brush pipe."));
	  gtk_object_sink (GTK_OBJECT (pipe));
	  return NULL;
	}
430 431 432 433

      if (pipe->nbrushes == 0)
	{
	  /* Replace name with the whole pipe's name */
Sven Neumann's avatar
Sven Neumann committed
434
	  g_free (GIMP_BRUSH (pipe)->name);
435 436
	  GIMP_BRUSH (pipe)->name = name;
	}
437

438
      pipe->brushes[pipe->nbrushes]->pixmap_mask = pattern->mask;
439
      pattern->mask = NULL;   /* #8150: mask now belongs to pixmap */
Sven Neumann's avatar
Sven Neumann committed
440
      g_free (pattern->name);
441 442
      pattern->name = NULL;   /* #8150: name no longer exists      */
                              
443
      pipe->nbrushes++;
444
    }
445 446

  /*  Clean up  */
447 448
  fclose (fp);

449
  g_free (pattern);
Michael Natterer's avatar
Michael Natterer committed
450

451 452 453 454
  return pipe;
}

GimpBrushPipe *
Michael Natterer's avatar
Michael Natterer committed
455
gimp_brush_pixmap_load (gchar *filename)
456 457
{
  GimpBrushPipe *pipe;
458 459
  GPattern      *pattern;
  FILE          *fp;
460 461 462 463

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

464
  pipe = GIMP_BRUSH_PIPE (gtk_type_new (GIMP_TYPE_BRUSH_PIPE));
465

466 467
  /* A (single) pixmap brush is a pixmap pipe brush with just one pixmap */
  pipe->dimension = 1;
Michael Natterer's avatar
Michael Natterer committed
468 469 470
  pipe->rank      = g_new (gint, 1);
  pipe->rank[0]   = 1;
  pipe->select    = g_new (PipeSelectModes, 1);
471
  pipe->select[0] = PIPE_SELECT_INCREMENTAL;
Michael Natterer's avatar
Michael Natterer committed
472 473
  pipe->index     = g_new (gint, 1);
  pipe->index[0]  = 0;
474

Michael Natterer's avatar
Michael Natterer committed
475
  pattern = g_new0 (GPattern, 1);
476

Michael Natterer's avatar
Michael Natterer committed
477
  pipe->brushes = g_new (GimpBrushPixmap *, 1);
478 479

  pipe->brushes[0] = GIMP_BRUSH_PIXMAP (pipe);
Michael Natterer's avatar
Michael Natterer committed
480
  pipe->current    = pipe->brushes[0];
481 482 483 484 485

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

  /* load the brush */
  if (!gimp_brush_load_brush (GIMP_BRUSH (pipe->brushes[0]),
486
			      fp, filename))
487
    {
Sven Neumann's avatar
Sven Neumann committed
488
      g_message (_("Failed to load pixmap brush."));
Sven Neumann's avatar
Sven Neumann committed
489
      pattern_free (pattern);
490
      gtk_object_sink (GTK_OBJECT (pipe));
491 492
      return NULL;
    }
Michael Natterer's avatar
Michael Natterer committed
493

494 495 496 497 498 499 500
  if (!pattern_load (pattern, fp, filename))
    {
      g_message (_("Failed to load pixmap brush."));
      gtk_object_sink (GTK_OBJECT (pipe));
      return NULL;
    }

501 502 503 504 505 506
  pipe->brushes[0]->pixmap_mask = pattern->mask;

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

Sven Neumann's avatar
Sven Neumann committed
507 508
  g_free (pattern->name);
  g_free (pattern);
Michael Natterer's avatar
Michael Natterer committed
509

510
  return pipe;
511 512 513 514 515 516 517 518 519 520
}

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;
}