gimpbrushpipe-load.c 10.7 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
#include "config.h"

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

27 28 29 30 31 32 33 34 35 36 37
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <fcntl.h>

#ifndef _O_BINARY
#define _O_BINARY 0
#endif

Sven Neumann's avatar
Sven Neumann committed
38 39
#include <gtk/gtk.h>

40 41 42 43
#ifdef G_OS_WIN32
#include <io.h>
#endif

44
#include "libgimpmath/gimpmath.h"
45
#include "libgimpbase/gimpparasiteio.h"
46

Michael Natterer's avatar
Michael Natterer committed
47
#include "core-types.h"
48

49
#include "gimpbrush.h"
50
#include "gimpbrush-header.h"
51
#include "gimpbrushpipe.h"
52
#include "gimppattern-header.h"
53
#include "gimprc.h"
54

55
/*  this needs to go away  */
56
#include "tools/gimppainttool.h"
57

58 59
#include "libgimp/gimpintl.h"

60

61 62
static GimpBrush * gimp_brush_pipe_select_brush     (GimpPaintTool *paint_tool);
static gboolean    gimp_brush_pipe_want_null_motion (GimpPaintTool *paint_tool);
63
static void        gimp_brush_pipe_destroy          (GtkObject *object);
64

65

66 67 68
static GimpBrushClass *parent_class = NULL;


69
static GimpBrush *
70
gimp_brush_pipe_select_brush (GimpPaintTool *paint_tool)
71
{
72
  GimpBrushPipe *pipe;
73 74
  gint           i, brushix, ix;
  gdouble        angle;
75

76 77
  g_return_val_if_fail (paint_tool != NULL, NULL);
  g_return_val_if_fail (GIMP_IS_BRUSH_PIPE (paint_tool->brush), NULL);
78

79
  pipe = GIMP_BRUSH_PIPE (paint_tool->brush);
80

81 82
  if (pipe->nbrushes == 1)
    return GIMP_BRUSH (pipe->current);
83

84 85 86 87 88 89
  brushix = 0;
  for (i = 0; i < pipe->dimension; i++)
    {
      switch (pipe->select[i])
	{
	case PIPE_SELECT_INCREMENTAL:
90
	  ix = (pipe->index[i] + 1) % pipe->rank[i];
91 92
	  break;
	case PIPE_SELECT_ANGULAR:
93 94
	  angle = atan2 (paint_tool->cury - paint_tool->lasty,
			 paint_tool->curx - paint_tool->lastx);
95 96 97
	  /* Offset angle to be compatible with PSP tubes */
	  angle += G_PI_2;
	  /* Map it to the [0..2*G_PI) interval */
98
	  if (angle < 0)
99 100 101 102
	    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]);
103 104 105
	  break;
	case PIPE_SELECT_RANDOM:
	  /* This probably isn't the right way */
106
	  ix = rand () % pipe->rank[i];
107 108
	  break;
	case PIPE_SELECT_PRESSURE:
109
	  ix = RINT (paint_tool->curpressure * (pipe->rank[i] - 1));
110 111
	  break;
	case PIPE_SELECT_TILT_X:
112
	  ix = RINT (paint_tool->curxtilt / 2.0 * pipe->rank[i]) + pipe->rank[i]/2;
113 114
	  break;
	case PIPE_SELECT_TILT_Y:
115
	  ix = RINT (paint_tool->curytilt / 2.0 * pipe->rank[i]) + pipe->rank[i]/2;
116
	  break;
Adam D. Moss's avatar
Adam D. Moss committed
117 118 119 120
	case PIPE_SELECT_CONSTANT:
	default:
	  ix = pipe->index[i];
	  break;
121
	}
122
      pipe->index[i] = CLAMP (ix, 0, pipe->rank[i]-1);
123
      brushix += pipe->stride[i] * pipe->index[i];
124 125
    }

126
  /* Make sure is inside bounds */
127
  brushix = CLAMP (brushix, 0, pipe->nbrushes-1);
128

129 130 131
  pipe->current = pipe->brushes[brushix];

  return GIMP_BRUSH (pipe->current);
132
}
133

134
static gboolean
135
gimp_brush_pipe_want_null_motion (GimpPaintTool *paint_tool)
136 137
{
  GimpBrushPipe *pipe;
138
  gint           i;
139

140 141
  g_return_val_if_fail (paint_tool != NULL, TRUE);
  g_return_val_if_fail (GIMP_IS_BRUSH_PIPE (paint_tool->brush), TRUE);
142

143
  pipe = GIMP_BRUSH_PIPE (paint_tool->brush);
144 145 146 147 148 149 150 151 152 153 154

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

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

  return TRUE;
}

155
static void
Michael Natterer's avatar
Michael Natterer committed
156
gimp_brush_pipe_destroy (GtkObject *object)
157
{
158
  GimpBrushPipe *pipe;
159
  gint           i;
160 161 162 163 164 165 166

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

  pipe = GIMP_BRUSH_PIPE (object);

  g_free (pipe->rank);
167
  g_free (pipe->stride);
168

169
  for (i = 0; i < pipe->nbrushes; i++)
170 171
    if (pipe->brushes[i])
      gtk_object_unref (GTK_OBJECT (pipe->brushes[i]));
172 173 174 175 176

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

177 178 179
  GIMP_BRUSH (pipe)->mask   = NULL;
  GIMP_BRUSH (pipe)->pixmap = NULL;

180 181
  if (GTK_OBJECT_CLASS (parent_class)->destroy)
    GTK_OBJECT_CLASS (parent_class)->destroy (object);
182 183 184 185 186 187
}

static void
gimp_brush_pipe_class_init (GimpBrushPipeClass *klass)
{
  GtkObjectClass *object_class;
188
  GimpBrushClass *brush_class;
Michael Natterer's avatar
Michael Natterer committed
189

190 191
  object_class = (GtkObjectClass *) klass;
  brush_class  = (GimpBrushClass *) klass;
192 193 194 195 196

  parent_class = gtk_type_class (GIMP_TYPE_BRUSH);

  brush_class->select_brush     = gimp_brush_pipe_select_brush;
  brush_class->want_null_motion = gimp_brush_pipe_want_null_motion;
197

Michael Natterer's avatar
Michael Natterer committed
198
  object_class->destroy = gimp_brush_pipe_destroy;
199 200 201
}

void
202
gimp_brush_pipe_init (GimpBrushPipe *pipe)
203
{
204
  pipe->current   = NULL;
205
  pipe->dimension = 0;
Michael Natterer's avatar
Michael Natterer committed
206
  pipe->rank      = NULL;
207
  pipe->stride    = NULL;
Michael Natterer's avatar
Michael Natterer committed
208
  pipe->nbrushes  = 0;
209
  pipe->brushes   = NULL;
Michael Natterer's avatar
Michael Natterer committed
210 211
  pipe->select    = NULL;
  pipe->index     = NULL;
212 213
}

214 215
GtkType
gimp_brush_pipe_get_type (void)
216
{
Michael Natterer's avatar
Michael Natterer committed
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
  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
      };

233
      type = gtk_type_unique (GIMP_TYPE_BRUSH, &info);
Michael Natterer's avatar
Michael Natterer committed
234 235
    }

236 237 238
  return type;
}

239
GimpData *
240
gimp_brush_pipe_load (const gchar *filename)
241
{
242
  GimpBrushPipe     *pipe = NULL;
243
  GimpPixPipeParams  params;
244 245 246 247 248 249 250
  gint               i;
  gint               num_of_brushes = 0;
  gint               totalcells;
  gchar             *paramstring;
  GString           *buffer;
  gchar              c;
  gint               fd;
251 252 253 254 255

  g_return_val_if_fail (filename != NULL, NULL);

  fd = open (filename, O_RDONLY | _O_BINARY);
  if (fd == -1)
256
    {
257
      g_message ("Couldn't open file '%s'", filename);
258 259
      return NULL;
    }
260

261
  /* The file format starts with a painfully simple text header */
262

263 264 265 266 267 268
  /*  get the name  */
  buffer = g_string_new (NULL);
  while (read (fd, &c, 1) == 1 && c != '\n' && buffer->len < 1024)
    g_string_append_c (buffer, c);
    
  if (buffer->len > 0 && buffer->len < 1024)
269
    {
270
      pipe = GIMP_BRUSH_PIPE (gtk_type_new (GIMP_TYPE_BRUSH_PIPE));      
271
      gimp_object_set_name (GIMP_OBJECT (pipe), buffer->str);
272
    }
273
  g_string_free (buffer, TRUE);
274 275 276 277 278 279

  if (!pipe)
    {
      g_message ("Couldn't read name for brush pipe from file '%s'\n", 
		 filename);
      close (fd);
280 281
      return NULL;
    }
282 283 284 285 286 287 288 289 290 291 292

  /*  get the number of brushes  */
  buffer = g_string_new (NULL);
  while (read (fd, &c, 1) == 1 && c != '\n' && buffer->len < 1024)
    g_string_append_c (buffer, c);

  if (buffer->len > 0 && buffer->len < 1024)
    {
      num_of_brushes = strtol (buffer->str, &paramstring, 10);
    }

293 294
  if (num_of_brushes < 1)
    {
295 296 297
      g_message (_("Brush pipes should have at least one brush:\n\"%s\""), 
		 filename);
      close (fd);
298
      gtk_object_sink (GTK_OBJECT (pipe));
299
      g_string_free (buffer, TRUE);
300 301
      return NULL;
    }
302

Michael Natterer's avatar
Michael Natterer committed
303
  while (*paramstring && isspace (*paramstring))
304
    paramstring++;
305

306
  if (*paramstring)
307
    {
308 309
      gimp_pixpipe_params_init (&params);
      gimp_pixpipe_params_parse (paramstring, &params);
310

311
      pipe->dimension = params.dim;
312 313 314 315
      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
316 317 318
      /* placement is not used at all ?? */
      if (params.free_placement_string)
	g_free (params.placement);
319

320 321
      for (i = 0; i < pipe->dimension; i++)
	{
322 323
	  pipe->rank[i] = params.rank[i];
	  if (strcmp (params.selection[i], "incremental") == 0)
324
	    pipe->select[i] = PIPE_SELECT_INCREMENTAL;
325
	  else if (strcmp (params.selection[i], "angular") == 0)
326
	    pipe->select[i] = PIPE_SELECT_ANGULAR;
327
	  else if (strcmp (params.selection[i], "velocity") == 0)
328
	    pipe->select[i] = PIPE_SELECT_VELOCITY;
329
	  else if (strcmp (params.selection[i], "random") == 0)
330
	    pipe->select[i] = PIPE_SELECT_RANDOM;
331
	  else if (strcmp (params.selection[i], "pressure") == 0)
332
	    pipe->select[i] = PIPE_SELECT_PRESSURE;
333
	  else if (strcmp (params.selection[i], "xtilt") == 0)
334
	    pipe->select[i] = PIPE_SELECT_TILT_X;
335
	  else if (strcmp (params.selection[i], "ytilt") == 0)
336 337 338
	    pipe->select[i] = PIPE_SELECT_TILT_Y;
	  else
	    pipe->select[i] = PIPE_SELECT_CONSTANT;
Sven Neumann's avatar
Sven Neumann committed
339 340
	  if (params.free_selection_string)
	    g_free (params.selection[i]);
341 342 343 344 345 346
	  pipe->index[i] = 0;
	}
    }
  else
    {
      pipe->dimension = 1;
Michael Natterer's avatar
Michael Natterer committed
347 348 349
      pipe->rank      = g_new (gint, 1);
      pipe->rank[0]   = num_of_brushes;
      pipe->select    = g_new (PipeSelectModes, 1);
350
      pipe->select[0] = PIPE_SELECT_INCREMENTAL;
Michael Natterer's avatar
Michael Natterer committed
351 352
      pipe->index     = g_new (gint, 1);
      pipe->index[0]  = 0;
353 354
    }

355 356
  g_string_free (buffer, TRUE);

357 358 359
  totalcells = 1;		/* Not all necessarily present, maybe */
  for (i = 0; i < pipe->dimension; i++)
    totalcells *= pipe->rank[i];
360
  pipe->stride = g_new0 (gint, pipe->dimension);
361 362 363 364 365 366 367 368
  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);
369

370
  pipe->brushes = g_new0 (GimpBrush *, num_of_brushes);
371 372 373

  while (pipe->nbrushes < num_of_brushes)
    {
374
      pipe->brushes[pipe->nbrushes] = gimp_brush_load_brush (fd, filename);
375

376 377
      if (pipe->brushes[pipe->nbrushes])
	{
378 379
	  gtk_object_ref (GTK_OBJECT (pipe->brushes[pipe->nbrushes]));
	  gtk_object_sink (GTK_OBJECT (pipe->brushes[pipe->nbrushes]));
380 381 382

	  gimp_object_set_name (GIMP_OBJECT (pipe->brushes[pipe->nbrushes]),
				NULL);
383
	}
384
      else
385
	{
386 387 388
	  g_message (_("Failed to load one of the brushes in the brush pipe\n\"%s\""), 
		       filename);
	  close (fd);
389 390 391
	  gtk_object_sink (GTK_OBJECT (pipe));
	  return NULL;
	}
392
  
393
      pipe->nbrushes++;
394
    }
395

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

399 400
  gimp_data_set_filename (GIMP_DATA (pipe), filename);

401
  /*  just to satisfy the code that relies on this crap  */
402 403 404 405 406
  GIMP_BRUSH (pipe)->spacing  = pipe->current->spacing;
  GIMP_BRUSH (pipe)->x_axis   = pipe->current->x_axis;
  GIMP_BRUSH (pipe)->y_axis   = pipe->current->y_axis;
  GIMP_BRUSH (pipe)->mask     = pipe->current->mask;
  GIMP_BRUSH (pipe)->pixmap   = pipe->current->pixmap;
407

408
  close (fd);
409

410
  return GIMP_DATA (pipe);
411
}