gimpbrushpipe-load.c 11.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
#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

38
#include <glib-object.h>
Sven Neumann's avatar
Sven Neumann committed
39

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

54
55
#include "libgimp/gimpintl.h"

56

57
58
59
60
61
static void        gimp_brush_pipe_class_init       (GimpBrushPipeClass *klass);
static void        gimp_brush_pipe_init             (GimpBrushPipe      *pipe);

static void        gimp_brush_pipe_finalize         (GObject       *object);

62
#if 0
63
64
static GimpBrush * gimp_brush_pipe_select_brush     (GimpPaintTool *paint_tool);
static gboolean    gimp_brush_pipe_want_null_motion (GimpPaintTool *paint_tool);
65
#endif
66

67

68
69
70
static GimpBrushClass *parent_class = NULL;


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
GType
gimp_brush_pipe_get_type (void)
{
  static GType brush_type = 0;

  if (! brush_type)
    {
      static const GTypeInfo brush_info =
      {
        sizeof (GimpBrushPipeClass),
	(GBaseInitFunc) NULL,
	(GBaseFinalizeFunc) NULL,
	(GClassInitFunc) gimp_brush_pipe_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data     */
	sizeof (GimpBrushPipe),
	0,              /* n_preallocs    */
	(GInstanceInitFunc) gimp_brush_pipe_init,
      };

      brush_type = g_type_register_static (GIMP_TYPE_BRUSH,
					   "GimpBrushPipe", 
					   &brush_info, 0);
    }

  return brush_type;
}

static void
gimp_brush_pipe_class_init (GimpBrushPipeClass *klass)
{
  GObjectClass   *object_class;
  GimpBrushClass *brush_class;

  object_class = G_OBJECT_CLASS (klass);
  brush_class  = GIMP_BRUSH_CLASS (klass);

  parent_class = g_type_class_peek_parent (klass);

  object_class->finalize        = gimp_brush_pipe_finalize;

112
113
114
115
#ifdef __GNUC__
#warning: FIXME brush_class->select_brush etc.
#endif
#if 0
116
117
  brush_class->select_brush     = gimp_brush_pipe_select_brush;
  brush_class->want_null_motion = gimp_brush_pipe_want_null_motion;
118
#endif
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
}

static void
gimp_brush_pipe_init (GimpBrushPipe *pipe)
{
  pipe->current   = NULL;
  pipe->dimension = 0;
  pipe->rank      = NULL;
  pipe->stride    = NULL;
  pipe->nbrushes  = 0;
  pipe->brushes   = NULL;
  pipe->select    = NULL;
  pipe->index     = NULL;
}

static void
gimp_brush_pipe_finalize (GObject *object)
{
  GimpBrushPipe *pipe;

  g_return_if_fail (GIMP_IS_BRUSH_PIPE (object));

  pipe = GIMP_BRUSH_PIPE (object);

  if (pipe->rank)
    {
      g_free (pipe->rank);
      pipe->rank = NULL;
    }
  if (pipe->stride)
    {
      g_free (pipe->stride);
      pipe->stride = NULL;
    }

  if (pipe->brushes)
    {
      gint i;

      for (i = 0; i < pipe->nbrushes; i++)
	if (pipe->brushes[i])
	  g_object_unref (G_OBJECT (pipe->brushes[i]));

      g_free (pipe->brushes);
      pipe->brushes = NULL;
    }

  if (pipe->select)
    {
      g_free (pipe->select);
      pipe->select = NULL;
    }
  if (pipe->index)
    {
      g_free (pipe->index);
      pipe->index = NULL;
    }

  GIMP_BRUSH (pipe)->mask   = NULL;
  GIMP_BRUSH (pipe)->pixmap = NULL;

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

183
#if 0
184
static GimpBrush *
185
gimp_brush_pipe_select_brush (GimpPaintTool *paint_tool)
186
{
187
  GimpBrushPipe *pipe;
188
189
  gint           i, brushix, ix;
  gdouble        angle;
190

191
192
  g_return_val_if_fail (paint_tool != NULL, NULL);
  g_return_val_if_fail (GIMP_IS_BRUSH_PIPE (paint_tool->brush), NULL);
193

194
  pipe = GIMP_BRUSH_PIPE (paint_tool->brush);
195

196
197
  if (pipe->nbrushes == 1)
    return GIMP_BRUSH (pipe->current);
198

199
200
201
202
203
204
  brushix = 0;
  for (i = 0; i < pipe->dimension; i++)
    {
      switch (pipe->select[i])
	{
	case PIPE_SELECT_INCREMENTAL:
205
	  ix = (pipe->index[i] + 1) % pipe->rank[i];
206
207
	  break;
	case PIPE_SELECT_ANGULAR:
208
209
	  angle = atan2 (paint_tool->cury - paint_tool->lasty,
			 paint_tool->curx - paint_tool->lastx);
210
211
212
	  /* Offset angle to be compatible with PSP tubes */
	  angle += G_PI_2;
	  /* Map it to the [0..2*G_PI) interval */
213
	  if (angle < 0)
214
215
216
217
	    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]);
218
219
220
	  break;
	case PIPE_SELECT_RANDOM:
	  /* This probably isn't the right way */
221
	  ix = rand () % pipe->rank[i];
222
223
	  break;
	case PIPE_SELECT_PRESSURE:
224
	  ix = RINT (paint_tool->curpressure * (pipe->rank[i] - 1));
225
226
	  break;
	case PIPE_SELECT_TILT_X:
227
	  ix = RINT (paint_tool->curxtilt / 2.0 * pipe->rank[i]) + pipe->rank[i]/2;
228
229
	  break;
	case PIPE_SELECT_TILT_Y:
230
	  ix = RINT (paint_tool->curytilt / 2.0 * pipe->rank[i]) + pipe->rank[i]/2;
231
	  break;
Adam D. Moss's avatar
Adam D. Moss committed
232
233
234
235
	case PIPE_SELECT_CONSTANT:
	default:
	  ix = pipe->index[i];
	  break;
236
	}
237
      pipe->index[i] = CLAMP (ix, 0, pipe->rank[i]-1);
238
      brushix += pipe->stride[i] * pipe->index[i];
239
240
    }

241
  /* Make sure is inside bounds */
242
  brushix = CLAMP (brushix, 0, pipe->nbrushes-1);
243

244
245
246
  pipe->current = pipe->brushes[brushix];

  return GIMP_BRUSH (pipe->current);
247
}
248

249
static gboolean
250
gimp_brush_pipe_want_null_motion (GimpPaintTool *paint_tool)
251
252
{
  GimpBrushPipe *pipe;
253
  gint           i;
254

255
256
  g_return_val_if_fail (paint_tool != NULL, TRUE);
  g_return_val_if_fail (GIMP_IS_BRUSH_PIPE (paint_tool->brush), TRUE);
257

258
  pipe = GIMP_BRUSH_PIPE (paint_tool->brush);
259
260
261
262
263
264
265
266
267
268

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

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

  return TRUE;
}
269
#endif
270

271
GimpData *
272
gimp_brush_pipe_load (const gchar *filename)
273
{
274
  GimpBrushPipe     *pipe = NULL;
275
  GimpPixPipeParams  params;
276
277
278
279
280
281
282
  gint               i;
  gint               num_of_brushes = 0;
  gint               totalcells;
  gchar             *paramstring;
  GString           *buffer;
  gchar              c;
  gint               fd;
283
284
285
286
287

  g_return_val_if_fail (filename != NULL, NULL);

  fd = open (filename, O_RDONLY | _O_BINARY);
  if (fd == -1)
288
    {
289
      g_message ("Couldn't open file '%s'", filename);
290
291
      return NULL;
    }
292

293
  /* The file format starts with a painfully simple text header */
294

295
296
297
298
299
300
  /*  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)
301
    {
Michael Natterer's avatar
Michael Natterer committed
302
      pipe = GIMP_BRUSH_PIPE (g_object_new (GIMP_TYPE_BRUSH_PIPE, NULL));
303
      gimp_object_set_name (GIMP_OBJECT (pipe), buffer->str);
304
    }
305
  g_string_free (buffer, TRUE);
306
307
308
309
310
311

  if (!pipe)
    {
      g_message ("Couldn't read name for brush pipe from file '%s'\n", 
		 filename);
      close (fd);
312
313
      return NULL;
    }
314
315
316
317
318
319
320
321
322
323
324

  /*  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);
    }

325
326
  if (num_of_brushes < 1)
    {
327
328
329
      g_message (_("Brush pipes should have at least one brush:\n\"%s\""), 
		 filename);
      close (fd);
330
      g_object_unref (G_OBJECT (pipe));
331
      g_string_free (buffer, TRUE);
332
333
      return NULL;
    }
334

Michael Natterer's avatar
Michael Natterer committed
335
  while (*paramstring && isspace (*paramstring))
336
    paramstring++;
337

338
  if (*paramstring)
339
    {
340
341
      gimp_pixpipe_params_init (&params);
      gimp_pixpipe_params_parse (paramstring, &params);
342

343
      pipe->dimension = params.dim;
344
345
346
347
      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
348
349
350
      /* placement is not used at all ?? */
      if (params.free_placement_string)
	g_free (params.placement);
351

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

387
388
  g_string_free (buffer, TRUE);

389
390
391
  totalcells = 1;		/* Not all necessarily present, maybe */
  for (i = 0; i < pipe->dimension; i++)
    totalcells *= pipe->rank[i];
392
  pipe->stride = g_new0 (gint, pipe->dimension);
393
394
395
396
397
398
399
400
  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);
401

402
  pipe->brushes = g_new0 (GimpBrush *, num_of_brushes);
403
404
405

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

408
409
      if (pipe->brushes[pipe->nbrushes])
	{
410
411
	  gimp_object_set_name (GIMP_OBJECT (pipe->brushes[pipe->nbrushes]),
				NULL);
412
	}
413
      else
414
	{
415
416
417
	  g_message (_("Failed to load one of the brushes in the brush pipe\n\"%s\""), 
		       filename);
	  close (fd);
418
	  g_object_unref (G_OBJECT (pipe));
419
420
	  return NULL;
	}
421
  
422
      pipe->nbrushes++;
423
    }
424

425
426
  /* Current brush is the first one. */
  pipe->current = pipe->brushes[0];
427

428
429
  gimp_data_set_filename (GIMP_DATA (pipe), filename);

430
  /*  just to satisfy the code that relies on this crap  */
431
432
433
434
435
  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;
436

437
  close (fd);
438

439
  return GIMP_DATA (pipe);
440
}