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

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
static void        gimp_brush_pipe_class_init       (GimpBrushPipeClass *klass);
static void        gimp_brush_pipe_init             (GimpBrushPipe      *pipe);

Michael Natterer's avatar
Michael Natterer committed
60
static void        gimp_brush_pipe_finalize         (GObject    *object);
61

Michael Natterer's avatar
Michael Natterer committed
62 63 64 65 66 67
static GimpBrush * gimp_brush_pipe_select_brush     (GimpBrush  *brush,
                                                     GimpCoords *last_coords,
                                                     GimpCoords *cur_coords);
static gboolean    gimp_brush_pipe_want_null_motion (GimpBrush  *brush,
                                                     GimpCoords *last_coords,
                                                     GimpCoords *cur_coords);
68

69

70 71 72
static GimpBrushClass *parent_class = NULL;


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 112 113 114 115 116 117 118 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
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;

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

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

180
static GimpBrush *
Michael Natterer's avatar
Michael Natterer committed
181 182 183
gimp_brush_pipe_select_brush (GimpBrush  *brush,
                              GimpCoords *last_coords,
                              GimpCoords *cur_coords)
184
{
185
  GimpBrushPipe *pipe;
186 187
  gint           i, brushix, ix;
  gdouble        angle;
188

Michael Natterer's avatar
Michael Natterer committed
189
  pipe = GIMP_BRUSH_PIPE (brush);
190

191 192
  if (pipe->nbrushes == 1)
    return GIMP_BRUSH (pipe->current);
193

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

236
  /* Make sure is inside bounds */
237
  brushix = CLAMP (brushix, 0, pipe->nbrushes-1);
238

239 240 241
  pipe->current = pipe->brushes[brushix];

  return GIMP_BRUSH (pipe->current);
242
}
243

244
static gboolean
Michael Natterer's avatar
Michael Natterer committed
245 246 247
gimp_brush_pipe_want_null_motion (GimpBrush  *brush,
                                  GimpCoords *last_coords,
                                  GimpCoords *cur_coords)
248 249
{
  GimpBrushPipe *pipe;
250
  gint           i;
251

Michael Natterer's avatar
Michael Natterer committed
252
  pipe = GIMP_BRUSH_PIPE (brush);
253 254 255 256 257 258 259 260 261 262 263

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

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

  return TRUE;
}

264
GimpData *
265
gimp_brush_pipe_load (const gchar *filename)
266
{
267
  GimpBrushPipe     *pipe = NULL;
268
  GimpPixPipeParams  params;
269 270 271 272 273 274 275
  gint               i;
  gint               num_of_brushes = 0;
  gint               totalcells;
  gchar             *paramstring;
  GString           *buffer;
  gchar              c;
  gint               fd;
276 277 278 279 280

  g_return_val_if_fail (filename != NULL, NULL);

  fd = open (filename, O_RDONLY | _O_BINARY);
  if (fd == -1)
281
    {
282
      g_message (_("Could not open file '%s'"), filename);
283 284
      return NULL;
    }
285

286
  /* The file format starts with a painfully simple text header */
287

288 289 290 291 292 293
  /*  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)
294
    {
Michael Natterer's avatar
Michael Natterer committed
295
      pipe = GIMP_BRUSH_PIPE (g_object_new (GIMP_TYPE_BRUSH_PIPE, NULL));
296 297 298 299 300 301 302

      if (g_utf8_validate (buffer->str, buffer->len, NULL))
        {
          gimp_object_set_name (GIMP_OBJECT (pipe), buffer->str);
        }
      else
        {
303
          g_message (_("Invalid UTF-8 string in GIMP brush file '%s'."), 
304 305 306
                     filename);
          gimp_object_set_name (GIMP_OBJECT (pipe), _("Unnamed"));
        }
307
    }
308
  g_string_free (buffer, TRUE);
309 310 311

  if (!pipe)
    {
312
      g_message (_("Fatal parsing error:\nBrush pipe file '%s' is corrupt."), 
313 314
		 filename);
      close (fd);
315 316
      return NULL;
    }
317 318 319 320 321 322 323 324 325 326 327

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

328 329
  if (num_of_brushes < 1)
    {
330
      g_message (_("Fatal parsing error:\nBrush pipe file '%s' is corrupt."), 
331 332
		 filename);
      close (fd);
333
      g_object_unref (G_OBJECT (pipe));
334
      g_string_free (buffer, TRUE);
335 336
      return NULL;
    }
337

Michael Natterer's avatar
Michael Natterer committed
338
  while (*paramstring && isspace (*paramstring))
339
    paramstring++;
340

341
  if (*paramstring)
342
    {
343 344
      gimp_pixpipe_params_init (&params);
      gimp_pixpipe_params_parse (paramstring, &params);
345

346
      pipe->dimension = params.dim;
347 348 349 350
      pipe->rank      = g_new0 (gint, pipe->dimension);
      pipe->select    = g_new0 (PipeSelectModes, pipe->dimension);
      pipe->index     = g_new0 (gint, pipe->dimension);

351 352 353
      /* placement is not used at all ?? */
      if (params.free_placement_string)
	g_free (params.placement);
354

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

390 391
  g_string_free (buffer, TRUE);

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

405
  pipe->brushes = g_new0 (GimpBrush *, num_of_brushes);
406 407 408

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

411 412
      if (pipe->brushes[pipe->nbrushes])
	{
413 414
	  gimp_object_set_name (GIMP_OBJECT (pipe->brushes[pipe->nbrushes]),
				NULL);
415
	}
416
      else
417
	{
418
	  g_message (_("Fatal parsing error:\nBrush pipe file '%s' is corrupt."), filename);
419
	  close (fd);
420
	  g_object_unref (G_OBJECT (pipe));
421 422
	  return NULL;
	}
423
  
424
      pipe->nbrushes++;
425
    }
426

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

430 431
  gimp_data_set_filename (GIMP_DATA (pipe), filename);

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

439
  close (fd);
440

441
  return GIMP_DATA (pipe);
442
}