gimpbrushgenerated-load.c 12.8 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
 *
 * gimp_brush_generated module Copyright 1998 Jay Cox <jaycox@earthlink.net>
 *
 * 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"

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

27 28 29 30
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

31
#include <gtk/gtk.h>
Sven Neumann's avatar
Sven Neumann committed
32

33 34
#include "libgimpmath/gimpmath.h"

Sven Neumann's avatar
Sven Neumann committed
35 36
#include "apptypes.h"

37 38
#include "appenv.h"
#include "gimpbrushgenerated.h"
39
#include "gimprc.h"
40
#include "gimpbrush.h"
41
#include "temp_buf.h"
42

43 44 45
/*  this needs to go away  */
#include "tools/paint_core.h"

46

47 48
#define OVERSAMPLING 5

49

50 51 52 53 54 55
/*  local function prototypes  */
static void     gimp_brush_generated_class_init (GimpBrushGeneratedClass *klass);
static void       gimp_brush_generated_init     (GimpBrushGenerated *brush);
static void       gimp_brush_generated_destroy  (GtkObject          *object);
static gboolean   gimp_brush_generated_save     (GimpData           *data);
static void       gimp_brush_generated_dirty    (GimpData           *data);
56

57

58
static GimpBrushClass *parent_class = NULL;
59

60

61
GtkType
62
gimp_brush_generated_get_type (void)
63
{
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
  static GtkType type = 0;

  if (!type)
    {
      GtkTypeInfo info =
      {
	"GimpBrushGenerated",
	sizeof (GimpBrushGenerated),
	sizeof (GimpBrushGeneratedClass),
	(GtkClassInitFunc) gimp_brush_generated_class_init,
	(GtkObjectInitFunc) gimp_brush_generated_init,
	/* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
	(GtkClassInitFunc) NULL
      };

80
      type = gtk_type_unique (GIMP_TYPE_BRUSH, &info);
81 82
    }

83 84 85
  return type;
}

86 87
static void
gimp_brush_generated_class_init (GimpBrushGeneratedClass *klass)
88
{
89 90
  GtkObjectClass *object_class;
  GimpDataClass  *data_class;
91

92 93
  object_class = (GtkObjectClass *) klass;
  data_class   = (GimpDataClass *) klass;
94

95
  parent_class = gtk_type_class (GIMP_TYPE_BRUSH);
96

97
  object_class->destroy = gimp_brush_generated_destroy;
98

99 100
  data_class->save  = gimp_brush_generated_save;
  data_class->dirty = gimp_brush_generated_dirty;
101 102
}

103 104
static void
gimp_brush_generated_init (GimpBrushGenerated *brush)
105
{
106 107 108 109 110
  brush->radius       = 5.0;
  brush->hardness     = 0.0;
  brush->angle        = 0.0;
  brush->aspect_ratio = 1.0;
  brush->freeze       = 0;
111 112
}

113 114
static void
gimp_brush_generated_destroy (GtkObject *object)
115
{
116 117 118
  if (GTK_OBJECT_CLASS (parent_class)->destroy)
    GTK_OBJECT_CLASS (parent_class)->destroy (object);
}
119

120 121 122 123 124
static gboolean
gimp_brush_generated_save (GimpData *data)
{
  GimpBrushGenerated *brush;
  FILE               *fp;
125

126 127
  g_return_val_if_fail (data != NULL, FALSE);
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (data), FALSE);
128

129
  brush = GIMP_BRUSH_GENERATED (data);
130 131

  /*  we are (finaly) ready to try to save the generated brush file  */
132
  if ((fp = fopen (data->filename, "wb")) == NULL)
133
    {
134 135
      g_warning ("Unable to save file %s", data->filename);
      return FALSE;
136 137 138 139 140 141 142 143 144
    }

  /* write magic header */
  fprintf (fp, "GIMP-VBR\n");

  /* write version */
  fprintf (fp, "1.0\n");

  /* write name */
145
  fprintf (fp, "%.255s\n", GIMP_OBJECT (brush)->name);
146 147

  /* write brush spacing */
148
  fprintf (fp, "%f\n", (gfloat) GIMP_BRUSH (brush)->spacing);
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163

  /* write brush radius */
  fprintf (fp, "%f\n", brush->radius);

  /* write brush hardness */
  fprintf (fp, "%f\n", brush->hardness);

  /* write brush aspect_ratio */
  fprintf (fp, "%f\n", brush->aspect_ratio);

  /* write brush angle */
  fprintf (fp, "%f\n", brush->angle);

  fclose (fp);

164
  return TRUE;
165
}
166

167 168
static double
gauss (gdouble f)
169
{ 
170 171
  /* this aint' a real gauss function */
  if (f < -.5)
172
    {
173
      f = -1.0 - f;
174
      return (2.0 * f*f);
175
    }
176

177
  if (f < .5)
178
    return (1.0 - 2.0 * f*f);
179

180
  f = 1.0 -f;
181
  return (2.0 * f*f);
182
}
183

184 185
static void
gimp_brush_generated_dirty (GimpData *data)
186
{
187
  GimpBrushGenerated *brush;
188
  register GimpBrush *gbrush = NULL;
189 190 191 192 193 194 195 196
  register gint       x, y;
  register guchar    *centerp;
  register gdouble    d;
  register gdouble    exponent;
  register guchar     a;
  register gint       length;
  register guchar    *lookup;
  register gdouble    sum, c, s, tx, ty;
197
  gdouble buffer[OVERSAMPLING];
198
  gint    width, height;
199

200 201
  brush = GIMP_BRUSH_GENERATED (data);

202
  if (brush->freeze) /* if we are frozen defer rerendering till later */
203
    return;
204

205
  gbrush = GIMP_BRUSH (brush);
206 207 208 209

  if (stingy_memory_use && gbrush->mask)
    temp_buf_unswap (gbrush->mask);

210
  if (gbrush->mask)
211
    {
212
      temp_buf_free (gbrush->mask);
213
    }
214

215
  /* compute the range of the brush. should do a better job than this? */
216 217
  s = sin (gimp_deg_to_rad (brush->angle));
  c = cos (gimp_deg_to_rad (brush->angle));
218

219 220 221 222 223 224 225 226
  tx = MAX (fabs (c*ceil (brush->radius) - s*ceil (brush->radius)
		  / brush->aspect_ratio), 
	    fabs (c*ceil (brush->radius) + s*ceil (brush->radius)
		  / brush->aspect_ratio));
  ty = MAX (fabs (s*ceil (brush->radius) + c*ceil (brush->radius)
		  / brush->aspect_ratio),
	    fabs (s*ceil (brush->radius) - c*ceil (brush->radius)
		  / brush->aspect_ratio));
227

228
  if (brush->radius > tx)
229
    width = ceil (tx);
230
  else
231
    width = ceil (brush->radius);
232

233
  if (brush->radius > ty)
234
    height = ceil (ty);
235
  else
236
    height = ceil (brush->radius);
237

238
  /* compute the axis for spacing */
239 240
  GIMP_BRUSH (brush)->x_axis.x =        c * brush->radius;
  GIMP_BRUSH (brush)->x_axis.y = -1.0 * s * brush->radius;
241

242 243
  GIMP_BRUSH (brush)->y_axis.x = (s * brush->radius / brush->aspect_ratio);
  GIMP_BRUSH (brush)->y_axis.y = (c * brush->radius / brush->aspect_ratio);
244
  
245 246
  gbrush->mask = temp_buf_new (width * 2 + 1,
			       height * 2 + 1,
247
			       1, width, height, 0);
248 249
  centerp = &gbrush->mask->data[height * gbrush->mask->width + width];

250 251 252 253
  if ((1.0 - brush->hardness) < 0.000001)
    exponent = 1000000; 
  else
    exponent = 1/(1.0 - brush->hardness);
254

255
  /* set up lookup table */
256
  length = ceil (sqrt (2 * ceil (brush->radius+1) * ceil (brush->radius+1))+1) * OVERSAMPLING;
257
  lookup = g_malloc (length);
258
  sum = 0.0;
259

260
  for (x = 0; x < OVERSAMPLING; x++)
261
    {
262
      d = fabs ((x + 0.5) / OVERSAMPLING - 0.5);
263
      if (d > brush->radius)
264 265 266 267 268 269 270 271 272 273 274 275
	buffer[x] = 0.0;
      else
	/* buffer[x] =  (1.0 - pow (d/brush->radius, exponent)); */
	buffer[x] = gauss (pow (d/brush->radius, exponent));
      sum += buffer[x];
    }

  for (x = 0; d < brush->radius || sum > 0.00001; d += 1.0 / OVERSAMPLING)
    {
      sum -= buffer[x % OVERSAMPLING];
      if (d > brush->radius)
	buffer[x % OVERSAMPLING] = 0.0;
276 277
      else
	/* buffer[x%OVERSAMPLING] =  (1.0 - pow (d/brush->radius, exponent)); */
278
	buffer[x % OVERSAMPLING] = gauss (pow (d/brush->radius, exponent));
279
      sum += buffer[x%OVERSAMPLING];
280
      lookup[x++] = RINT (sum * (255.0 / OVERSAMPLING));
281
    }
282
  while (x < length)
283 284 285 286
    {
      lookup[x++] = 0;
    }
  /* compute one half and mirror it */
287 288
  for (y = 0; y <= height; y++)
    {
289 290 291 292 293 294 295
      for (x = -width; x <= width; x++)
	{
	  tx = c*x - s*y;
	  ty = c*y + s*x;
	  ty *= brush->aspect_ratio;
	  d = sqrt (tx*tx + ty*ty);
	  if (d < brush->radius+1)
296
	    a = lookup[(gint) RINT (d * OVERSAMPLING)];
297 298 299 300 301
	  else
	    a = 0;
	  centerp[   y*gbrush->mask->width + x] = a;
	  centerp[-1*y*gbrush->mask->width - x] = a;
	}
302
    }
303

304
  g_free (lookup);
305

306 307 308
  if (GIMP_DATA_CLASS (parent_class)->dirty)
    GIMP_DATA_CLASS (parent_class)->dirty (data);

309
  gimp_viewable_invalidate_preview (GIMP_VIEWABLE (brush));
310 311
}

312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 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 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
GimpBrush *
gimp_brush_generated_new (gfloat radius,
			  gfloat hardness,
			  gfloat angle,
			  gfloat aspect_ratio)
{
  GimpBrushGenerated *brush;

  /* set up normal brush data */
  brush = GIMP_BRUSH_GENERATED (gtk_type_new (GIMP_TYPE_BRUSH_GENERATED));

  gimp_object_set_name (GIMP_OBJECT (brush), "Untitled");

  GIMP_BRUSH (brush)->spacing = 20;

  /* set up gimp_brush_generated data */
  brush->radius       = radius;
  brush->hardness     = hardness;
  brush->angle        = angle;
  brush->aspect_ratio = aspect_ratio;

  /* render brush mask */
  gimp_data_dirty (GIMP_DATA (brush));

  return GIMP_BRUSH (brush);
}

GimpBrush *
gimp_brush_generated_load (const gchar *filename)
{
  GimpBrushGenerated *brush;
  FILE   *fp;
  gchar   string[256];
  gfloat  fl;
  gfloat  version;

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

  /* make sure the file we are reading is the right type */
  fgets (string, 255, fp);
  
  if (strncmp (string, "GIMP-VBR", 8) != 0)
    return NULL;
  
  /* make sure we are reading a compatible version */
  fgets (string, 255, fp);
  sscanf (string, "%f", &version);
  g_return_val_if_fail (version < 2.0, NULL);

  /* create new brush */
  brush = GIMP_BRUSH_GENERATED (gtk_type_new (GIMP_TYPE_BRUSH_GENERATED));

  gimp_data_set_filename (GIMP_DATA (brush), filename);

  gimp_brush_generated_freeze (brush);

  /* read name */
  fgets (string, 255, fp);
  if (string[strlen (string) - 1] == '\n')
    string[strlen (string) - 1] = 0;
  gimp_object_set_name (GIMP_OBJECT (brush), string);

  /* read brush spacing */
  fscanf (fp, "%f", &fl);
  GIMP_BRUSH (brush)->spacing = fl;

  /* read brush radius */
  fscanf (fp, "%f", &fl);
  gimp_brush_generated_set_radius (brush, fl);

  /* read brush hardness */
  fscanf (fp, "%f", &fl);
  gimp_brush_generated_set_hardness (brush, fl);

  /* read brush aspect_ratio */
  fscanf (fp, "%f", &fl);
  gimp_brush_generated_set_aspect_ratio (brush, fl);

  /* read brush angle */
  fscanf (fp, "%f", &fl);
  gimp_brush_generated_set_angle (brush, fl);

  fclose (fp);

  gimp_brush_generated_thaw (brush);

  GIMP_DATA (brush)->dirty = FALSE;

  if (stingy_memory_use)
    temp_buf_swap (GIMP_BRUSH (brush)->mask);

  return GIMP_BRUSH (brush);
}

void
gimp_brush_generated_freeze (GimpBrushGenerated *brush)
{
  g_return_if_fail (brush != NULL);
  g_return_if_fail (GIMP_IS_BRUSH_GENERATED (brush));

  brush->freeze++;
}

void
gimp_brush_generated_thaw (GimpBrushGenerated *brush)
{
  g_return_if_fail (brush != NULL);
  g_return_if_fail (GIMP_IS_BRUSH_GENERATED (brush));
  
  if (brush->freeze > 0)
    brush->freeze--;

  if (brush->freeze == 0)
    gimp_data_dirty (GIMP_DATA (brush));
}

429 430 431
gfloat
gimp_brush_generated_set_radius (GimpBrushGenerated *brush,
				 gfloat              radius)
432
{
433 434
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

435 436
  if (radius < 0.0)
    radius = 0.0;
437
  else if (radius > 32767.0)
438
    radius = 32767.0;
439

440 441
  if (radius == brush->radius)
    return radius;
442

443
  brush->radius = radius;
444

445 446
  if (! brush->freeze)
    gimp_data_dirty (GIMP_DATA (brush));
447

448 449 450
  return brush->radius;
}

451 452 453
gfloat
gimp_brush_generated_set_hardness (GimpBrushGenerated *brush,
				   gfloat              hardness)
454
{
455 456
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

457 458
  if (hardness < 0.0)
    hardness = 0.0;
459
  else if (hardness > 1.0)
460
    hardness = 1.0;
461

462 463
  if (brush->hardness == hardness)
    return hardness;
464

465
  brush->hardness = hardness;
466

467
  if (!brush->freeze)
468
    gimp_data_dirty (GIMP_DATA (brush));
469

470 471 472
  return brush->hardness;
}

473 474 475
gfloat
gimp_brush_generated_set_angle (GimpBrushGenerated *brush,
				gfloat              angle)
476
{
477 478
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

479
  if (angle < 0.0)
480
    angle = -1.0 * fmod (angle, 180.0);
481
  else if (angle > 180.0)
482
    angle = fmod (angle, 180.0);
483

484 485
  if (brush->angle == angle)
    return angle;
486

487
  brush->angle = angle;
488

489
  if (!brush->freeze)
490
    gimp_data_dirty (GIMP_DATA (brush));
491

492 493 494
  return brush->angle;
}

495 496 497
gfloat
gimp_brush_generated_set_aspect_ratio (GimpBrushGenerated *brush,
				       gfloat              ratio)
498
{
499 500
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

501 502
  if (ratio < 1.0)
    ratio = 1.0;
503
  else if (ratio > 1000)
504
    ratio = 1000;
505

506 507
  if (brush->aspect_ratio == ratio)
    return ratio;
508

509
  brush->aspect_ratio = ratio;
510

511
  if (!brush->freeze)
512
    gimp_data_dirty (GIMP_DATA (brush));
513

514 515 516
  return brush->aspect_ratio;
}

517 518
gfloat
gimp_brush_generated_get_radius (const GimpBrushGenerated *brush)
519
{
520 521
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

522 523 524
  return brush->radius;
}

525
gfloat
526
gimp_brush_generated_get_hardness (const GimpBrushGenerated *brush)
527
{
528 529
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

530 531 532
  return brush->hardness;
}

533
gfloat
534
gimp_brush_generated_get_angle (const GimpBrushGenerated *brush)
535
{
536 537
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

538 539 540
  return brush->angle;
}

541
gfloat
542
gimp_brush_generated_get_aspect_ratio (const GimpBrushGenerated *brush)
543
{
544 545
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

546 547
  return brush->aspect_ratio;
}