73
61
GParamSpec *pspec);
74
static gboolean gimp_brush_generated_save (GimpData *data,
76
62
static void gimp_brush_generated_dirty (GimpData *data);
77
63
static gchar * gimp_brush_generated_get_extension (GimpData *data);
78
static GimpData * gimp_brush_generated_duplicate (GimpData *data,
79
gboolean stingy_memory_use);
82
static GimpBrushClass *parent_class = NULL;
86
gimp_brush_generated_get_type (void)
88
static GType brush_type = 0;
92
static const GTypeInfo brush_info =
94
sizeof (GimpBrushGeneratedClass),
96
(GBaseFinalizeFunc) NULL,
97
(GClassInitFunc) gimp_brush_generated_class_init,
98
NULL, /* class_finalize */
99
NULL, /* class_data */
100
sizeof (GimpBrushGenerated),
102
(GInstanceInitFunc) gimp_brush_generated_init,
105
brush_type = g_type_register_static (GIMP_TYPE_BRUSH,
106
"GimpBrushGenerated",
64
static GimpData * gimp_brush_generated_duplicate (GimpData *data);
65
static TempBuf * gimp_brush_generated_scale_mask (GimpBrush *gbrush,
67
static void gimp_brush_generated_get_half_size (GimpBrushGenerated *gbrush,
68
GimpBrushGeneratedShape shape,
73
gdouble angle_in_degrees,
79
GimpVector2 *_y_axis);
80
static void gimp_brush_generated_real_scale_size
87
G_DEFINE_TYPE (GimpBrushGenerated, gimp_brush_generated, GIMP_TYPE_BRUSH)
89
#define parent_class gimp_brush_generated_parent_class
114
93
gimp_brush_generated_class_init (GimpBrushGeneratedClass *klass)
116
GObjectClass *object_class = G_OBJECT_CLASS (klass);
117
GimpDataClass *data_class = GIMP_DATA_CLASS (klass);
119
parent_class = g_type_class_peek_parent (klass);
121
object_class->set_property = gimp_brush_generated_set_property;
122
object_class->get_property = gimp_brush_generated_get_property;
124
data_class->save = gimp_brush_generated_save;
125
data_class->dirty = gimp_brush_generated_dirty;
126
data_class->get_extension = gimp_brush_generated_get_extension;
127
data_class->duplicate = gimp_brush_generated_duplicate;
95
GObjectClass *object_class = G_OBJECT_CLASS (klass);
96
GimpDataClass *data_class = GIMP_DATA_CLASS (klass);
97
GimpBrushClass *brush_class = GIMP_BRUSH_CLASS (klass);
99
object_class->set_property = gimp_brush_generated_set_property;
100
object_class->get_property = gimp_brush_generated_get_property;
102
data_class->save = gimp_brush_generated_save;
103
data_class->dirty = gimp_brush_generated_dirty;
104
data_class->get_extension = gimp_brush_generated_get_extension;
105
data_class->duplicate = gimp_brush_generated_duplicate;
107
brush_class->scale_size = gimp_brush_generated_real_scale_size;
108
brush_class->scale_mask = gimp_brush_generated_scale_mask;
129
110
g_object_class_install_property (object_class, PROP_SHAPE,
130
111
g_param_spec_enum ("shape", NULL, NULL,
131
112
GIMP_TYPE_BRUSH_GENERATED_SHAPE,
132
113
GIMP_BRUSH_GENERATED_CIRCLE,
114
GIMP_PARAM_READWRITE |
134
115
G_PARAM_CONSTRUCT));
135
116
g_object_class_install_property (object_class, PROP_RADIUS,
136
117
g_param_spec_double ("radius", NULL, NULL,
119
GIMP_PARAM_READWRITE |
139
120
G_PARAM_CONSTRUCT));
140
121
g_object_class_install_property (object_class, PROP_SPIKES,
141
122
g_param_spec_int ("spikes", NULL, NULL,
124
GIMP_PARAM_READWRITE |
144
125
G_PARAM_CONSTRUCT));
145
126
g_object_class_install_property (object_class, PROP_HARDNESS,
146
127
g_param_spec_double ("hardness", NULL, NULL,
129
GIMP_PARAM_READWRITE |
149
130
G_PARAM_CONSTRUCT));
150
131
g_object_class_install_property (object_class, PROP_ASPECT_RATIO,
151
132
g_param_spec_double ("aspect-ratio",
135
GIMP_PARAM_READWRITE |
155
136
G_PARAM_CONSTRUCT));
156
137
g_object_class_install_property (object_class, PROP_ANGLE,
157
138
g_param_spec_double ("angle", NULL, NULL,
140
GIMP_PARAM_READWRITE |
160
141
G_PARAM_CONSTRUCT));
242
gimp_brush_generated_save (GimpData *data,
245
GimpBrushGenerated *brush = GIMP_BRUSH_GENERATED (data);
247
gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
248
gboolean have_shape = FALSE;
250
file = fopen (data->filename, "wb");
254
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_OPEN,
255
_("Could not open '%s' for writing: %s"),
256
gimp_filename_to_utf8 (data->filename),
261
/* write magic header */
262
fprintf (file, "GIMP-VBR\n");
265
if (brush->shape != GIMP_BRUSH_GENERATED_CIRCLE || brush->spikes > 2)
267
fprintf (file, "1.5\n");
272
fprintf (file, "1.0\n");
276
fprintf (file, "%.255s\n", GIMP_OBJECT (brush)->name);
280
GEnumClass *enum_class;
281
GEnumValue *shape_val;
283
enum_class = g_type_class_peek (GIMP_TYPE_BRUSH_GENERATED_SHAPE);
286
shape_val = g_enum_get_value (enum_class, brush->shape);
287
fprintf (file, "%s\n", shape_val->value_nick);
290
/* write brush spacing */
291
fprintf (file, "%s\n",
292
g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%f",
293
GIMP_BRUSH (brush)->spacing));
295
/* write brush radius */
296
fprintf (file, "%s\n",
297
g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%f",
302
/* write brush spikes */
303
fprintf (file, "%d\n", brush->spikes);
306
/* write brush hardness */
307
fprintf (file, "%s\n",
308
g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%f",
311
/* write brush aspect_ratio */
312
fprintf (file, "%s\n",
313
g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%f",
314
brush->aspect_ratio));
316
/* write brush angle */
317
fprintf (file, "%s\n",
318
g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, "%f",
327
223
gimp_brush_generated_get_extension (GimpData *data)
362
256
return (2.0 * f*f);
366
gimp_brush_generated_dirty (GimpData *data)
259
/* set up lookup table */
261
gimp_brush_generated_calc_lut (gdouble radius,
368
GimpBrushGenerated *brush = GIMP_BRUSH_GENERATED (data);
369
GimpBrush *gbrush = GIMP_BRUSH (brush);
380
gdouble c, s, cs, ss;
381
gdouble short_radius;
382
gdouble buffer[OVERSAMPLING];
385
temp_buf_free (gbrush->mask);
387
s = sin (gimp_deg_to_rad (brush->angle));
388
c = cos (gimp_deg_to_rad (brush->angle));
390
short_radius = brush->radius / brush->aspect_ratio;
392
gbrush->x_axis.x = c * brush->radius;
393
gbrush->x_axis.y = -1.0 * s * brush->radius;
394
gbrush->y_axis.x = s * short_radius;
395
gbrush->y_axis.y = c * short_radius;
397
switch (brush->shape)
399
case GIMP_BRUSH_GENERATED_CIRCLE:
400
width = ceil (sqrt (gbrush->x_axis.x * gbrush->x_axis.x +
401
gbrush->y_axis.x * gbrush->y_axis.x));
402
height = ceil (sqrt (gbrush->x_axis.y * gbrush->x_axis.y +
403
gbrush->y_axis.y * gbrush->y_axis.y));
406
case GIMP_BRUSH_GENERATED_SQUARE:
407
width = ceil (fabs (gbrush->x_axis.x) + fabs (gbrush->y_axis.x));
408
height = ceil (fabs (gbrush->x_axis.y) + fabs (gbrush->y_axis.y));
411
case GIMP_BRUSH_GENERATED_DIAMOND:
412
width = ceil (MAX (fabs (gbrush->x_axis.x), fabs (gbrush->y_axis.x)));
413
height = ceil (MAX (fabs (gbrush->x_axis.y), fabs (gbrush->y_axis.y)));
417
g_return_if_reached ();
420
if (brush->spikes > 2)
422
/* could be optimized by respecting the angle */
423
width = height = ceil (sqrt (brush->radius * brush->radius +
424
short_radius * short_radius));
425
gbrush->y_axis.x = s * brush->radius;
426
gbrush->y_axis.y = c * brush->radius;
429
gbrush->mask = temp_buf_new (width * 2 + 1,
431
1, width, height, NULL);
433
centerp = temp_buf_data (gbrush->mask) + height * gbrush->mask->width + width;
435
/* set up lookup table */
436
length = OVERSAMPLING * ceil (1 + sqrt (2 *
437
ceil (brush->radius + 1.0) *
438
ceil (brush->radius + 1.0)));
440
if ((1.0 - brush->hardness) < 0.0000004)
441
exponent = 1000000.0;
443
exponent = 0.4 / (1.0 - brush->hardness);
270
gdouble buffer[OVERSAMPLING];
272
length = OVERSAMPLING * ceil (1 + sqrt (2 * SQR (ceil (radius + 1.0))));
445
274
lookup = g_malloc (length);
277
if ((1.0 - hardness) < 0.0000004)
278
exponent = 1000000.0;
280
exponent = 0.4 / (1.0 - hardness);
448
282
for (x = 0; x < OVERSAMPLING; x++)
450
284
d = fabs ((x + 0.5) / OVERSAMPLING - 0.5);
452
if (d > brush->radius)
455
buffer[x] = gauss (pow (d / brush->radius, exponent));
289
buffer[x] = gauss (pow (d / radius, exponent));
457
291
sum += buffer[x];
460
for (x = 0; d < brush->radius || sum > 0.00001; d += 1.0 / OVERSAMPLING)
294
for (x = 0; d < radius || sum > 0.00001; d += 1.0 / OVERSAMPLING)
462
296
sum -= buffer[x % OVERSAMPLING];
464
if (d > brush->radius)
465
299
buffer[x % OVERSAMPLING] = 0.0;
467
buffer[x % OVERSAMPLING] = gauss (pow (d / brush->radius, exponent));
301
buffer[x % OVERSAMPLING] = gauss (pow (d / radius, exponent));
469
303
sum += buffer[x % OVERSAMPLING];
470
304
lookup[x++] = RINT (sum * (255.0 / OVERSAMPLING));
478
cs = cos (- 2 * G_PI / brush->spikes);
479
ss = sin (- 2 * G_PI / brush->spikes);
316
gimp_brush_generated_calc (GimpBrushGenerated *brush,
317
GimpBrushGeneratedShape shape,
330
gint half_height = 0;
332
gdouble c, s, cs, ss;
337
gimp_brush_generated_get_half_size (brush,
344
&half_width, &half_height,
345
&s, &c, &x_axis, &y_axis);
347
mask = temp_buf_new (half_width * 2 + 1,
349
1, half_width, half_height, NULL);
351
centerp = temp_buf_data (mask) + half_height * mask->width + half_width;
353
lookup = gimp_brush_generated_calc_lut (radius, hardness);
355
cs = cos (- 2 * G_PI / spikes);
356
ss = sin (- 2 * G_PI / spikes);
481
358
/* for an even number of spikes compute one half and mirror it */
482
for (y = (brush->spikes % 2 ? -height : 0); y <= height; y++)
359
for (y = (spikes % 2 ? -half_height : 0); y <= half_height; y++)
484
for (x = -width; x <= width; x++)
361
for (x = -half_width; x <= half_width; x++)
486
gdouble tx, ty, angle;
489
ty = fabs (s*x + c*y);
491
if (brush->spikes > 2)
364
gdouble tx = c * x - s * y;
365
gdouble ty = fabs (s * x + c * y);
493
angle = atan2 (ty, tx);
369
gdouble angle = atan2 (ty, tx);
495
while (angle > G_PI / brush->spikes)
371
while (angle > G_PI / spikes)
497
gdouble sx = tx, sy = ty;
499
376
tx = cs * sx - ss * sy;
500
377
ty = ss * sx + cs * sy;
502
angle -= 2 * G_PI / brush->spikes;
379
angle -= 2 * G_PI / spikes;
506
ty *= brush->aspect_ratio;
507
switch (brush->shape)
509
387
case GIMP_BRUSH_GENERATED_CIRCLE:
510
d = sqrt (tx*tx + ty*ty);
388
d = sqrt (SQR (tx) + SQR (ty));
512
390
case GIMP_BRUSH_GENERATED_SQUARE:
513
391
d = MAX (fabs (tx), fabs (ty));
520
if (d < brush->radius + 1)
521
399
a = lookup[(gint) RINT (d * OVERSAMPLING)];
525
centerp[ y * gbrush->mask->width + x] = a;
403
centerp[y * mask->width + x] = a;
527
if (brush->spikes % 2 == 0)
528
centerp[-1 * y * gbrush->mask->width - x] = a;
406
centerp[-1 * y * mask->width - x] = a;
534
if (GIMP_DATA_CLASS (parent_class)->dirty)
535
GIMP_DATA_CLASS (parent_class)->dirty (data);
422
gimp_brush_generated_dirty (GimpData *data)
424
GimpBrushGenerated *brush = GIMP_BRUSH_GENERATED (data);
425
GimpBrush *gbrush = GIMP_BRUSH (brush);
428
temp_buf_free (gbrush->mask);
430
gbrush->mask = gimp_brush_generated_calc (brush,
440
GIMP_DATA_CLASS (parent_class)->dirty (data);
443
/* This function is shared between gimp_brush_scale_size and
444
* gimp_brush_generated_calc, therefore we provide a bunch of optional
445
* pointers for returnvalues.
448
gimp_brush_generated_get_half_size (GimpBrushGenerated *gbrush,
449
GimpBrushGeneratedShape shape,
454
gdouble angle_in_degrees,
459
GimpVector2 *_x_axis,
460
GimpVector2 *_y_axis)
463
gdouble short_radius;
467
s = sin (gimp_deg_to_rad (angle_in_degrees));
468
c = cos (gimp_deg_to_rad (angle_in_degrees));
470
short_radius = radius / aspect_ratio;
472
x_axis.x = c * radius;
473
x_axis.y = -1.0 * s * radius;
474
y_axis.x = s * short_radius;
475
y_axis.y = c * short_radius;
479
case GIMP_BRUSH_GENERATED_CIRCLE:
480
*half_width = ceil (sqrt (x_axis.x * x_axis.x + y_axis.x * y_axis.x));
481
*half_height = ceil (sqrt (x_axis.y * x_axis.y + y_axis.y * y_axis.y));
484
case GIMP_BRUSH_GENERATED_SQUARE:
485
*half_width = ceil (fabs (x_axis.x) + fabs (y_axis.x));
486
*half_height = ceil (fabs (x_axis.y) + fabs (y_axis.y));
489
case GIMP_BRUSH_GENERATED_DIAMOND:
490
*half_width = ceil (MAX (fabs (x_axis.x), fabs (y_axis.x)));
491
*half_height = ceil (MAX (fabs (x_axis.y), fabs (y_axis.y)));
497
/* could be optimized by respecting the angle */
498
*half_width = *half_height = ceil (sqrt (radius * radius +
499
short_radius * short_radius));
500
y_axis.x = s * radius;
501
y_axis.y = c * radius;
504
/* These will typically be set then this function is called by
505
* gimp_brush_generated_calc, which needs the values in its algorithms.
521
gimp_brush_generated_real_scale_size (GimpBrush *gbrush,
526
GimpBrushGenerated *brush = GIMP_BRUSH_GENERATED (gbrush);
530
gimp_brush_generated_get_half_size (brush,
532
brush->radius * scale,
537
&half_width, &half_height,
538
NULL, NULL, NULL, NULL);
540
*width = half_width * 2 + 1;
541
*height = half_height * 2 + 1;
545
gimp_brush_generated_scale_mask (GimpBrush *gbrush,
548
GimpBrushGenerated *brush = GIMP_BRUSH_GENERATED (gbrush);
550
return gimp_brush_generated_calc (brush,
552
brush->radius * scale,
563
GIMP_BRUSH (brush)->spacing = 20;
565
/* render brush mask */
566
gimp_data_dirty (GIMP_DATA (brush));
568
if (stingy_memory_use)
569
temp_buf_swap (GIMP_BRUSH (brush)->mask);
571
586
return GIMP_DATA (brush);
575
gimp_brush_generated_load (const gchar *filename,
576
gboolean stingy_memory_use,
579
GimpBrushGenerated *brush;
583
GimpBrushGeneratedShape shape = GIMP_BRUSH_GENERATED_CIRCLE;
584
gboolean have_shape = FALSE;
589
gdouble aspect_ratio;
592
g_return_val_if_fail (filename != NULL, NULL);
593
g_return_val_if_fail (g_path_is_absolute (filename), NULL);
594
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
596
file = fopen (filename, "rb");
600
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_OPEN,
601
_("Could not open '%s' for reading: %s"),
602
gimp_filename_to_utf8 (filename), g_strerror (errno));
606
/* make sure the file we are reading is the right type */
608
if (! fgets (string, sizeof (string), file))
611
if (strncmp (string, "GIMP-VBR", 8) != 0)
613
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
614
_("Fatal parse error in brush file '%s': "
615
"Not a GIMP brush file."),
616
gimp_filename_to_utf8 (filename));
620
/* make sure we are reading a compatible version */
622
if (! fgets (string, sizeof (string), file))
625
if (strncmp (string, "1.0", 3))
627
if (strncmp (string, "1.5", 3))
629
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
630
_("Fatal parse error in brush file '%s': "
631
"Unknown GIMP brush version."),
632
gimp_filename_to_utf8 (filename));
643
if (! fgets (string, sizeof (string), file))
647
name = gimp_any_to_utf8 (string, -1,
648
_("Invalid UTF-8 string in brush file '%s'."),
649
gimp_filename_to_utf8 (filename));
653
GEnumClass *enum_class;
654
GEnumValue *shape_val;
656
enum_class = g_type_class_peek (GIMP_TYPE_BRUSH_GENERATED_SHAPE);
660
if (! fgets (string, sizeof (string), file))
664
shape_val = g_enum_get_value_by_nick (enum_class, string);
668
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
669
_("Fatal parse error in brush file '%s': "
670
"Unknown GIMP brush shape."),
671
gimp_filename_to_utf8 (filename));
675
shape = shape_val->value;
678
/* read brush spacing */
680
if (! fgets (string, sizeof (string), file))
682
spacing = g_ascii_strtod (string, NULL);
684
/* read brush radius */
686
if (! fgets (string, sizeof (string), file))
688
radius = g_ascii_strtod (string, NULL);
692
/* read brush radius */
694
if (! fgets (string, sizeof (string), file))
696
spikes = CLAMP (atoi (string), 2, 20);
699
/* read brush hardness */
701
if (! fgets (string, sizeof (string), file))
703
hardness = g_ascii_strtod (string, NULL);
705
/* read brush aspect_ratio */
707
if (! fgets (string, sizeof (string), file))
709
aspect_ratio = g_ascii_strtod (string, NULL);
711
/* read brush angle */
713
if (! fgets (string, sizeof (string), file))
715
angle = g_ascii_strtod (string, NULL);
719
/* create new brush */
720
brush = g_object_new (GIMP_TYPE_BRUSH_GENERATED,
725
"hardness", hardness,
726
"aspect-ratio", aspect_ratio,
731
GIMP_BRUSH (brush)->spacing = spacing;
733
/* render brush mask */
734
gimp_data_dirty (GIMP_DATA (brush));
736
if (stingy_memory_use)
737
temp_buf_swap (GIMP_BRUSH (brush)->mask);
739
return g_list_prepend (NULL, brush);
748
if (error && *error == NULL)
749
g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ,
750
_("Error while reading brush file '%s': %s"),
751
gimp_filename_to_utf8 (filename),
752
errno ? g_strerror (errno) : _("File is truncated"));
757
589
GimpBrushGeneratedShape
758
590
gimp_brush_generated_set_shape (GimpBrushGenerated *brush,
759
591
GimpBrushGeneratedShape shape)