gimpbrushgenerated-load.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
 *
 * 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
static gchar    * gimp_brush_generated_get_extension (GimpData      *data);
57

58

59
static GimpBrushClass *parent_class = NULL;
60

61

62
GtkType
63
gimp_brush_generated_get_type (void)
64
{
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
  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
      };

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

84 85 86
  return type;
}

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

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

96
  parent_class = gtk_type_class (GIMP_TYPE_BRUSH);
97

98
  object_class->destroy = gimp_brush_generated_destroy;
99

100 101 102
  data_class->save          = gimp_brush_generated_save;
  data_class->dirty         = gimp_brush_generated_dirty;
  data_class->get_extension = gimp_brush_generated_get_extension;
103 104
}

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

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

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

128 129
  g_return_val_if_fail (data != NULL, FALSE);
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (data), FALSE);
130

131
  brush = GIMP_BRUSH_GENERATED (data);
132 133

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

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

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

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

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

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

166
  return TRUE;
167
}
168

169 170 171 172 173 174
static gchar *
gimp_brush_generated_get_extension (GimpData *data)
{
  return GIMP_BRUSH_GENERATED_FILE_EXTENSION;
}

175 176
static double
gauss (gdouble f)
177
{ 
178 179
  /* this aint' a real gauss function */
  if (f < -.5)
180
    {
181
      f = -1.0 - f;
182
      return (2.0 * f*f);
183
    }
184

185
  if (f < .5)
186
    return (1.0 - 2.0 * f*f);
187

188
  f = 1.0 -f;
189
  return (2.0 * f*f);
190
}
191

192 193
static void
gimp_brush_generated_dirty (GimpData *data)
194
{
195
  GimpBrushGenerated *brush;
196
  register GimpBrush *gbrush = NULL;
197 198 199 200 201 202 203 204
  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;
205
  gdouble buffer[OVERSAMPLING];
206
  gint    width, height;
207

208 209
  brush = GIMP_BRUSH_GENERATED (data);

210
  if (brush->freeze) /* if we are frozen defer rerendering till later */
211
    return;
212

213
  gbrush = GIMP_BRUSH (brush);
214 215 216 217

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

218
  if (gbrush->mask)
219
    {
220
      temp_buf_free (gbrush->mask);
221
    }
222

223
  /* compute the range of the brush. should do a better job than this? */
224 225
  s = sin (gimp_deg_to_rad (brush->angle));
  c = cos (gimp_deg_to_rad (brush->angle));
226

227 228 229 230 231 232 233 234
  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));
235

236
  if (brush->radius > tx)
237
    width = ceil (tx);
238
  else
239
    width = ceil (brush->radius);
240

241
  if (brush->radius > ty)
242
    height = ceil (ty);
243
  else
244
    height = ceil (brush->radius);
245

246
  /* compute the axis for spacing */
247 248
  GIMP_BRUSH (brush)->x_axis.x =        c * brush->radius;
  GIMP_BRUSH (brush)->x_axis.y = -1.0 * s * brush->radius;
249

250 251
  GIMP_BRUSH (brush)->y_axis.x = (s * brush->radius / brush->aspect_ratio);
  GIMP_BRUSH (brush)->y_axis.y = (c * brush->radius / brush->aspect_ratio);
252
  
253 254
  gbrush->mask = temp_buf_new (width * 2 + 1,
			       height * 2 + 1,
255
			       1, width, height, 0);
256 257
  centerp = &gbrush->mask->data[height * gbrush->mask->width + width];

258 259 260 261
  if ((1.0 - brush->hardness) < 0.000001)
    exponent = 1000000; 
  else
    exponent = 1/(1.0 - brush->hardness);
262

263
  /* set up lookup table */
264
  length = ceil (sqrt (2 * ceil (brush->radius+1) * ceil (brush->radius+1))+1) * OVERSAMPLING;
265
  lookup = g_malloc (length);
266
  sum = 0.0;
267

268
  for (x = 0; x < OVERSAMPLING; x++)
269
    {
270
      d = fabs ((x + 0.5) / OVERSAMPLING - 0.5);
271
      if (d > brush->radius)
272 273 274 275 276 277 278 279 280 281 282 283
	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;
284 285
      else
	/* buffer[x%OVERSAMPLING] =  (1.0 - pow (d/brush->radius, exponent)); */
286
	buffer[x % OVERSAMPLING] = gauss (pow (d/brush->radius, exponent));
287
      sum += buffer[x%OVERSAMPLING];
288
      lookup[x++] = RINT (sum * (255.0 / OVERSAMPLING));
289
    }
290
  while (x < length)
291 292 293 294
    {
      lookup[x++] = 0;
    }
  /* compute one half and mirror it */
295 296
  for (y = 0; y <= height; y++)
    {
297 298 299 300 301 302 303
      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)
304
	    a = lookup[(gint) RINT (d * OVERSAMPLING)];
305 306 307 308 309
	  else
	    a = 0;
	  centerp[   y*gbrush->mask->width + x] = a;
	  centerp[-1*y*gbrush->mask->width - x] = a;
	}
310
    }
311

312
  g_free (lookup);
313

314 315
  if (GIMP_DATA_CLASS (parent_class)->dirty)
    GIMP_DATA_CLASS (parent_class)->dirty (data);
316 317
}

318
GimpData *
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
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));

342
  return GIMP_DATA (brush);
343 344
}

345
GimpData *
346 347 348
gimp_brush_generated_load (const gchar *filename)
{
  GimpBrushGenerated *brush;
349 350 351 352
  FILE               *fp;
  gchar               string[256];
  gfloat              fl;
  gfloat              version;
353 354 355 356 357 358 359 360 361

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

410
  return GIMP_DATA (brush);
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
}

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

435 436 437
gfloat
gimp_brush_generated_set_radius (GimpBrushGenerated *brush,
				 gfloat              radius)
438
{
439 440
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

441 442
  if (radius < 0.0)
    radius = 0.0;
443
  else if (radius > 32767.0)
444
    radius = 32767.0;
445

446 447
  if (radius == brush->radius)
    return radius;
448

449
  brush->radius = radius;
450

451 452
  if (! brush->freeze)
    gimp_data_dirty (GIMP_DATA (brush));
453

454 455 456
  return brush->radius;
}

457 458 459
gfloat
gimp_brush_generated_set_hardness (GimpBrushGenerated *brush,
				   gfloat              hardness)
460
{
461 462
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

463 464
  if (hardness < 0.0)
    hardness = 0.0;
465
  else if (hardness > 1.0)
466
    hardness = 1.0;
467

468 469
  if (brush->hardness == hardness)
    return hardness;
470

471
  brush->hardness = hardness;
472

473
  if (!brush->freeze)
474
    gimp_data_dirty (GIMP_DATA (brush));
475

476 477 478
  return brush->hardness;
}

479 480 481
gfloat
gimp_brush_generated_set_angle (GimpBrushGenerated *brush,
				gfloat              angle)
482
{
483 484
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

485
  if (angle < 0.0)
486
    angle = -1.0 * fmod (angle, 180.0);
487
  else if (angle > 180.0)
488
    angle = fmod (angle, 180.0);
489

490 491
  if (brush->angle == angle)
    return angle;
492

493
  brush->angle = angle;
494

495
  if (!brush->freeze)
496
    gimp_data_dirty (GIMP_DATA (brush));
497

498 499 500
  return brush->angle;
}

501 502 503
gfloat
gimp_brush_generated_set_aspect_ratio (GimpBrushGenerated *brush,
				       gfloat              ratio)
504
{
505 506
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

507 508
  if (ratio < 1.0)
    ratio = 1.0;
509
  else if (ratio > 1000)
510
    ratio = 1000;
511

512 513
  if (brush->aspect_ratio == ratio)
    return ratio;
514

515
  brush->aspect_ratio = ratio;
516

517
  if (!brush->freeze)
518
    gimp_data_dirty (GIMP_DATA (brush));
519

520 521 522
  return brush->aspect_ratio;
}

523 524
gfloat
gimp_brush_generated_get_radius (const GimpBrushGenerated *brush)
525
{
526 527
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

528 529 530
  return brush->radius;
}

531
gfloat
532
gimp_brush_generated_get_hardness (const GimpBrushGenerated *brush)
533
{
534 535
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

536 537 538
  return brush->hardness;
}

539
gfloat
540
gimp_brush_generated_get_angle (const GimpBrushGenerated *brush)
541
{
542 543
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

544 545 546
  return brush->angle;
}

547
gfloat
548
gimp_brush_generated_get_aspect_ratio (const GimpBrushGenerated *brush)
549
{
550 551
  g_return_val_if_fail (GIMP_IS_BRUSH_GENERATED (brush), -1.0);

552 553
  return brush->aspect_ratio;
}