1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * Largely based on gimpdrawable-gradient.c
5 *
6 * gimpoperationgradient.c
7 * Copyright (C) 2014 Michael Henning <drawoc@darkrefraction.com>
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23 #include "config.h"
24
25 #include <cairo.h>
26 #include <gegl.h>
27 #include <gdk-pixbuf/gdk-pixbuf.h>
28
29 #include "libgimpcolor/gimpcolor.h"
30 #include "libgimpmath/gimpmath.h"
31
32 #include "operations-types.h"
33
34 #include "core/gimpgradient.h"
35
36 #include "gimpoperationgradient.h"
37
38
39 #define GRADIENT_CACHE_N_SUPERSAMPLES 4
40 #define GRADIENT_CACHE_MAX_SIZE ((1 << 20) / sizeof (GimpRGB))
41
42
43 enum
44 {
45 PROP_0,
46 PROP_CONTEXT,
47 PROP_GRADIENT,
48 PROP_START_X,
49 PROP_START_Y,
50 PROP_END_X,
51 PROP_END_Y,
52 PROP_GRADIENT_TYPE,
53 PROP_GRADIENT_REPEAT,
54 PROP_OFFSET,
55 PROP_GRADIENT_REVERSE,
56 PROP_GRADIENT_BLEND_COLOR_SPACE,
57 PROP_SUPERSAMPLE,
58 PROP_SUPERSAMPLE_DEPTH,
59 PROP_SUPERSAMPLE_THRESHOLD,
60 PROP_DITHER
61 };
62
63 typedef struct
64 {
65 GimpGradient *gradient;
66 gboolean reverse;
67 GimpGradientBlendColorSpace blend_color_space;
68 GimpRGB *gradient_cache;
69 gint gradient_cache_size;
70 GimpGradientSegment *last_seg;
71 gdouble offset;
72 gdouble sx, sy;
73 GimpGradientType gradient_type;
74 gdouble dist;
75 gdouble vec[2];
76 GimpRepeatMode repeat;
77 GeglSampler *dist_sampler;
78 } RenderBlendData;
79
80
81 typedef struct
82 {
83 gfloat *data;
84 GeglRectangle roi;
85 GRand *dither_rand;
86 } PutPixelData;
87
88
89 /* local function prototypes */
90
91 static void gimp_operation_gradient_dispose (GObject *gobject);
92 static void gimp_operation_gradient_finalize (GObject *gobject);
93 static void gimp_operation_gradient_get_property (GObject *object,
94 guint property_id,
95 GValue *value,
96 GParamSpec *pspec);
97 static void gimp_operation_gradient_set_property (GObject *object,
98 guint property_id,
99 const GValue *value,
100 GParamSpec *pspec);
101
102 static void gimp_operation_gradient_prepare (GeglOperation *operation);
103
104 static GeglRectangle gimp_operation_gradient_get_bounding_box (GeglOperation *operation);
105
106 static gdouble gradient_calc_conical_sym_factor (gdouble dist,
107 gdouble *axis,
108 gdouble offset,
109 gdouble x,
110 gdouble y);
111 static gdouble gradient_calc_conical_asym_factor (gdouble dist,
112 gdouble *axis,
113 gdouble offset,
114 gdouble x,
115 gdouble y);
116 static gdouble gradient_calc_square_factor (gdouble dist,
117 gdouble offset,
118 gdouble x,
119 gdouble y);
120 static gdouble gradient_calc_radial_factor (gdouble dist,
121 gdouble offset,
122 gdouble x,
123 gdouble y);
124 static gdouble gradient_calc_linear_factor (gdouble dist,
125 gdouble *vec,
126 gdouble offset,
127 gdouble x,
128 gdouble y);
129 static gdouble gradient_calc_bilinear_factor (gdouble dist,
130 gdouble *vec,
131 gdouble offset,
132 gdouble x,
133 gdouble y);
134 static gdouble gradient_calc_spiral_factor (gdouble dist,
135 gdouble *axis,
136 gdouble offset,
137 gdouble x,
138 gdouble y,
139 gboolean clockwise);
140
141 static gdouble gradient_calc_shapeburst_angular_factor (GeglSampler *dist_sampler,
142 gdouble offset,
143 gdouble x,
144 gdouble y);
145 static gdouble gradient_calc_shapeburst_spherical_factor (GeglSampler *dist_sampler,
146 gdouble offset,
147 gdouble x,
148 gdouble y);
149 static gdouble gradient_calc_shapeburst_dimpled_factor (GeglSampler *dist_sampler,
150 gdouble offset,
151 gdouble x,
152 gdouble y);
153
154 static void gradient_render_pixel (gdouble x,
155 gdouble y,
156 GimpRGB *color,
157 gpointer render_data);
158
159 static void gradient_put_pixel (gint x,
160 gint y,
161 GimpRGB *color,
162 gpointer put_pixel_data);
163
164 static void gradient_dither_pixel (GimpRGB *color,
165 GRand *dither_rand,
166 gfloat *dest);
167
168 static gboolean gimp_operation_gradient_process (GeglOperation *operation,
169 GeglBuffer *input,
170 GeglBuffer *output,
171 const GeglRectangle *result,
172 gint level);
173
174 static void gimp_operation_gradient_invalidate_cache (GimpOperationGradient *self);
175 static void gimp_operation_gradient_validate_cache (GimpOperationGradient *self);
176
177
G_DEFINE_TYPE(GimpOperationGradient,gimp_operation_gradient,GEGL_TYPE_OPERATION_FILTER)178 G_DEFINE_TYPE (GimpOperationGradient, gimp_operation_gradient,
179 GEGL_TYPE_OPERATION_FILTER)
180
181 #define parent_class gimp_operation_gradient_parent_class
182
183
184 static void
185 gimp_operation_gradient_class_init (GimpOperationGradientClass *klass)
186 {
187 GObjectClass *object_class = G_OBJECT_CLASS (klass);
188 GeglOperationClass *operation_class = GEGL_OPERATION_CLASS (klass);
189 GeglOperationFilterClass *filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
190
191 object_class->dispose = gimp_operation_gradient_dispose;
192 object_class->finalize = gimp_operation_gradient_finalize;
193 object_class->set_property = gimp_operation_gradient_set_property;
194 object_class->get_property = gimp_operation_gradient_get_property;
195
196 operation_class->prepare = gimp_operation_gradient_prepare;
197 operation_class->get_bounding_box = gimp_operation_gradient_get_bounding_box;
198
199 filter_class->process = gimp_operation_gradient_process;
200
201 gegl_operation_class_set_keys (operation_class,
202 "name", "gimp:gradient",
203 "categories", "gimp",
204 "description", "GIMP Gradient operation",
205 NULL);
206
207 g_object_class_install_property (object_class, PROP_CONTEXT,
208 g_param_spec_object ("context",
209 "Context",
210 "A GimpContext",
211 GIMP_TYPE_OBJECT,
212 G_PARAM_READWRITE |
213 G_PARAM_CONSTRUCT));
214
215 g_object_class_install_property (object_class, PROP_GRADIENT,
216 g_param_spec_object ("gradient",
217 "Gradient",
218 "A GimpGradient to render",
219 GIMP_TYPE_OBJECT,
220 G_PARAM_READWRITE |
221 G_PARAM_CONSTRUCT));
222
223 g_object_class_install_property (object_class, PROP_START_X,
224 g_param_spec_double ("start-x",
225 "Start X",
226 "X coordinate of the first point",
227 -G_MAXDOUBLE, G_MAXDOUBLE, 0,
228 G_PARAM_READWRITE |
229 G_PARAM_CONSTRUCT));
230
231 g_object_class_install_property (object_class, PROP_START_Y,
232 g_param_spec_double ("start-y",
233 "Start Y",
234 "Y coordinate of the first point",
235 -G_MAXDOUBLE, G_MAXDOUBLE, 0,
236 G_PARAM_READWRITE |
237 G_PARAM_CONSTRUCT));
238
239 g_object_class_install_property (object_class, PROP_END_X,
240 g_param_spec_double ("end-x",
241 "End X",
242 "X coordinate of the second point",
243 -G_MAXDOUBLE, G_MAXDOUBLE, 200,
244 G_PARAM_READWRITE |
245 G_PARAM_CONSTRUCT));
246
247 g_object_class_install_property (object_class, PROP_END_Y,
248 g_param_spec_double ("end-y",
249 "End Y",
250 "Y coordinate of the second point",
251 -G_MAXDOUBLE, G_MAXDOUBLE, 200,
252 G_PARAM_READWRITE |
253 G_PARAM_CONSTRUCT));
254
255 g_object_class_install_property (object_class, PROP_GRADIENT_TYPE,
256 g_param_spec_enum ("gradient-type",
257 "Gradient Type",
258 "The type of gradient to render",
259 GIMP_TYPE_GRADIENT_TYPE,
260 GIMP_GRADIENT_LINEAR,
261 G_PARAM_READWRITE |
262 G_PARAM_CONSTRUCT));
263
264 g_object_class_install_property (object_class, PROP_GRADIENT_REPEAT,
265 g_param_spec_enum ("gradient-repeat",
266 "Repeat mode",
267 "Repeat mode",
268 GIMP_TYPE_REPEAT_MODE,
269 GIMP_REPEAT_NONE,
270 G_PARAM_READWRITE |
271 G_PARAM_CONSTRUCT));
272
273 g_object_class_install_property (object_class, PROP_OFFSET,
274 g_param_spec_double ("offset",
275 "Offset",
276 "Offset relates to the starting and ending coordinates "
277 "specified for the blend. This parameter is mode dependent.",
278 0, G_MAXDOUBLE, 0,
279 G_PARAM_READWRITE |
280 G_PARAM_CONSTRUCT));
281
282 g_object_class_install_property (object_class, PROP_GRADIENT_REVERSE,
283 g_param_spec_boolean ("gradient-reverse",
284 "Reverse",
285 "Reverse the gradient",
286 FALSE,
287 G_PARAM_READWRITE |
288 G_PARAM_CONSTRUCT));
289
290 g_object_class_install_property (object_class, PROP_GRADIENT_BLEND_COLOR_SPACE,
291 g_param_spec_enum ("gradient-blend-color-space",
292 "Blend Color Space",
293 "Which color space to use when blending RGB gradient segments",
294 GIMP_TYPE_GRADIENT_BLEND_COLOR_SPACE,
295 GIMP_GRADIENT_BLEND_RGB_PERCEPTUAL,
296 G_PARAM_READWRITE |
297 G_PARAM_CONSTRUCT));
298
299 g_object_class_install_property (object_class, PROP_SUPERSAMPLE,
300 g_param_spec_boolean ("supersample",
301 "Supersample",
302 "Do adaptive supersampling",
303 FALSE,
304 G_PARAM_READWRITE |
305 G_PARAM_CONSTRUCT));
306
307 g_object_class_install_property (object_class, PROP_SUPERSAMPLE_DEPTH,
308 g_param_spec_int ("supersample-depth",
309 "Max depth",
310 "Maximum recursion levels for supersampling",
311 1, 9, 3,
312 G_PARAM_READWRITE |
313 G_PARAM_CONSTRUCT));
314
315 g_object_class_install_property (object_class, PROP_SUPERSAMPLE_THRESHOLD,
316 g_param_spec_double ("supersample-threshold",
317 "Threshold",
318 "Supersampling threshold",
319 0, 4, 0.20,
320 G_PARAM_READWRITE |
321 G_PARAM_CONSTRUCT));
322
323 g_object_class_install_property (object_class, PROP_DITHER,
324 g_param_spec_boolean ("dither",
325 "Dither",
326 "Use dithering to reduce banding",
327 FALSE,
328 G_PARAM_READWRITE |
329 G_PARAM_CONSTRUCT));
330 }
331
332 static void
gimp_operation_gradient_init(GimpOperationGradient * self)333 gimp_operation_gradient_init (GimpOperationGradient *self)
334 {
335 g_mutex_init (&self->gradient_cache_mutex);
336 }
337
338 static void
gimp_operation_gradient_dispose(GObject * object)339 gimp_operation_gradient_dispose (GObject *object)
340 {
341 GimpOperationGradient *self = GIMP_OPERATION_GRADIENT (object);
342
343 gimp_operation_gradient_invalidate_cache (self);
344
345 g_clear_object (&self->gradient);
346 g_clear_object (&self->context);
347
348 G_OBJECT_CLASS (parent_class)->dispose (object);
349 }
350
351 static void
gimp_operation_gradient_finalize(GObject * object)352 gimp_operation_gradient_finalize (GObject *object)
353 {
354 GimpOperationGradient *self = GIMP_OPERATION_GRADIENT (object);
355
356 g_mutex_clear (&self->gradient_cache_mutex);
357
358 G_OBJECT_CLASS (parent_class)->finalize (object);
359 }
360
361 static void
gimp_operation_gradient_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)362 gimp_operation_gradient_get_property (GObject *object,
363 guint property_id,
364 GValue *value,
365 GParamSpec *pspec)
366 {
367 GimpOperationGradient *self = GIMP_OPERATION_GRADIENT (object);
368
369 switch (property_id)
370 {
371 case PROP_CONTEXT:
372 g_value_set_object (value, self->context);
373 break;
374
375 case PROP_GRADIENT:
376 g_value_set_object (value, self->gradient);
377 break;
378
379 case PROP_START_X:
380 g_value_set_double (value, self->start_x);
381 break;
382
383 case PROP_START_Y:
384 g_value_set_double (value, self->start_y);
385 break;
386
387 case PROP_END_X:
388 g_value_set_double (value, self->end_x);
389 break;
390
391 case PROP_END_Y:
392 g_value_set_double (value, self->end_y);
393 break;
394
395 case PROP_GRADIENT_TYPE:
396 g_value_set_enum (value, self->gradient_type);
397 break;
398
399 case PROP_GRADIENT_REPEAT:
400 g_value_set_enum (value, self->gradient_repeat);
401 break;
402
403 case PROP_OFFSET:
404 g_value_set_double (value, self->offset);
405 break;
406
407 case PROP_GRADIENT_REVERSE:
408 g_value_set_boolean (value, self->gradient_reverse);
409 break;
410
411 case PROP_GRADIENT_BLEND_COLOR_SPACE:
412 g_value_set_enum (value, self->gradient_blend_color_space);
413 break;
414
415 case PROP_SUPERSAMPLE:
416 g_value_set_boolean (value, self->supersample);
417 break;
418
419 case PROP_SUPERSAMPLE_DEPTH:
420 g_value_set_int (value, self->supersample_depth);
421 break;
422
423 case PROP_SUPERSAMPLE_THRESHOLD:
424 g_value_set_double (value, self->supersample_threshold);
425 break;
426
427 case PROP_DITHER:
428 g_value_set_boolean (value, self->dither);
429 break;
430
431 default:
432 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
433 break;
434 }
435 }
436
437 static void
gimp_operation_gradient_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)438 gimp_operation_gradient_set_property (GObject *object,
439 guint property_id,
440 const GValue *value,
441 GParamSpec *pspec)
442 {
443 GimpOperationGradient *self = GIMP_OPERATION_GRADIENT (object);
444
445 switch (property_id)
446 {
447 case PROP_CONTEXT:
448 if (self->context)
449 g_object_unref (self->context);
450
451 self->context = g_value_dup_object (value);
452 break;
453
454 case PROP_GRADIENT:
455 {
456 GimpGradient *gradient = g_value_get_object (value);
457
458 g_clear_object (&self->gradient);
459
460 if (gradient)
461 {
462 if (gimp_gradient_has_fg_bg_segments (gradient))
463 self->gradient = gimp_gradient_flatten (gradient, self->context);
464 else
465 self->gradient = g_object_ref (gradient);
466 }
467
468 gimp_operation_gradient_invalidate_cache (self);
469 }
470 break;
471
472 case PROP_START_X:
473 self->start_x = g_value_get_double (value);
474
475 gimp_operation_gradient_invalidate_cache (self);
476 break;
477
478 case PROP_START_Y:
479 self->start_y = g_value_get_double (value);
480
481 gimp_operation_gradient_invalidate_cache (self);
482 break;
483
484 case PROP_END_X:
485 self->end_x = g_value_get_double (value);
486
487 gimp_operation_gradient_invalidate_cache (self);
488 break;
489
490 case PROP_END_Y:
491 self->end_y = g_value_get_double (value);
492
493 gimp_operation_gradient_invalidate_cache (self);
494 break;
495
496 case PROP_GRADIENT_TYPE:
497 self->gradient_type = g_value_get_enum (value);
498 break;
499
500 case PROP_GRADIENT_REPEAT:
501 self->gradient_repeat = g_value_get_enum (value);
502 break;
503
504 case PROP_OFFSET:
505 self->offset = g_value_get_double (value);
506 break;
507
508 case PROP_GRADIENT_REVERSE:
509 self->gradient_reverse = g_value_get_boolean (value);
510
511 gimp_operation_gradient_invalidate_cache (self);
512 break;
513
514 case PROP_GRADIENT_BLEND_COLOR_SPACE:
515 self->gradient_blend_color_space = g_value_get_enum (value);
516
517 gimp_operation_gradient_invalidate_cache (self);
518 break;
519
520 case PROP_SUPERSAMPLE:
521 self->supersample = g_value_get_boolean (value);
522 break;
523
524 case PROP_SUPERSAMPLE_DEPTH:
525 self->supersample_depth = g_value_get_int (value);
526 break;
527
528 case PROP_SUPERSAMPLE_THRESHOLD:
529 self->supersample_threshold = g_value_get_double (value);
530 break;
531
532 case PROP_DITHER:
533 self->dither = g_value_get_boolean (value);
534 break;
535
536 default:
537 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
538 break;
539 }
540 }
541
542 static void
gimp_operation_gradient_prepare(GeglOperation * operation)543 gimp_operation_gradient_prepare (GeglOperation *operation)
544 {
545 gegl_operation_set_format (operation, "output", babl_format ("R'G'B'A float"));
546 }
547
548 static GeglRectangle
gimp_operation_gradient_get_bounding_box(GeglOperation * operation)549 gimp_operation_gradient_get_bounding_box (GeglOperation *operation)
550 {
551 return gegl_rectangle_infinite_plane ();
552 }
553
554 static gdouble
gradient_calc_conical_sym_factor(gdouble dist,gdouble * axis,gdouble offset,gdouble x,gdouble y)555 gradient_calc_conical_sym_factor (gdouble dist,
556 gdouble *axis,
557 gdouble offset,
558 gdouble x,
559 gdouble y)
560 {
561 if (dist == 0.0)
562 {
563 return 0.0;
564 }
565 else if ((x != 0) || (y != 0))
566 {
567 gdouble vec[2];
568 gdouble r;
569 gdouble rat;
570
571 /* Calculate offset from the start in pixels */
572
573 r = sqrt (SQR (x) + SQR (y));
574
575 vec[0] = x / r;
576 vec[1] = y / r;
577
578 rat = axis[0] * vec[0] + axis[1] * vec[1]; /* Dot product */
579
580 if (rat > 1.0)
581 rat = 1.0;
582 else if (rat < -1.0)
583 rat = -1.0;
584
585 /* This cool idea is courtesy Josh MacDonald,
586 * Ali Rahimi --- two more XCF losers. */
587
588 rat = acos (rat) / G_PI;
589 rat = pow (rat, (offset / 10.0) + 1.0);
590
591 return CLAMP (rat, 0.0, 1.0);
592 }
593 else
594 {
595 return 0.5;
596 }
597 }
598
599 static gdouble
gradient_calc_conical_asym_factor(gdouble dist,gdouble * axis,gdouble offset,gdouble x,gdouble y)600 gradient_calc_conical_asym_factor (gdouble dist,
601 gdouble *axis,
602 gdouble offset,
603 gdouble x,
604 gdouble y)
605 {
606 if (dist == 0.0)
607 {
608 return 0.0;
609 }
610 else if (x != 0 || y != 0)
611 {
612 gdouble ang0, ang1;
613 gdouble ang;
614 gdouble rat;
615
616 ang0 = atan2 (axis[0], axis[1]) + G_PI;
617
618 ang1 = atan2 (x, y) + G_PI;
619
620 ang = ang1 - ang0;
621
622 if (ang < 0.0)
623 ang += (2.0 * G_PI);
624
625 rat = ang / (2.0 * G_PI);
626 rat = pow (rat, (offset / 10.0) + 1.0);
627
628 return CLAMP (rat, 0.0, 1.0);
629 }
630 else
631 {
632 return 0.5; /* We are on middle point */
633 }
634 }
635
636 static gdouble
gradient_calc_square_factor(gdouble dist,gdouble offset,gdouble x,gdouble y)637 gradient_calc_square_factor (gdouble dist,
638 gdouble offset,
639 gdouble x,
640 gdouble y)
641 {
642 if (dist == 0.0)
643 {
644 return 0.0;
645 }
646 else
647 {
648 gdouble r;
649 gdouble rat;
650
651 /* Calculate offset from start as a value in [0, 1] */
652
653 offset = offset / 100.0;
654
655 r = MAX (fabs (x), fabs (y));
656 rat = r / dist;
657
658 if (rat < offset)
659 return 0.0;
660 else if (offset == 1.0)
661 return (rat >= 1.0) ? 1.0 : 0.0;
662 else
663 return (rat - offset) / (1.0 - offset);
664 }
665 }
666
667 static gdouble
gradient_calc_radial_factor(gdouble dist,gdouble offset,gdouble x,gdouble y)668 gradient_calc_radial_factor (gdouble dist,
669 gdouble offset,
670 gdouble x,
671 gdouble y)
672 {
673 if (dist == 0.0)
674 {
675 return 0.0;
676 }
677 else
678 {
679 gdouble r;
680 gdouble rat;
681
682 /* Calculate radial offset from start as a value in [0, 1] */
683
684 offset = offset / 100.0;
685
686 r = sqrt (SQR (x) + SQR (y));
687 rat = r / dist;
688
689 if (rat < offset)
690 return 0.0;
691 else if (offset == 1.0)
692 return (rat >= 1.0) ? 1.0 : 0.0;
693 else
694 return (rat - offset) / (1.0 - offset);
695 }
696 }
697
698 static gdouble
gradient_calc_linear_factor(gdouble dist,gdouble * vec,gdouble offset,gdouble x,gdouble y)699 gradient_calc_linear_factor (gdouble dist,
700 gdouble *vec,
701 gdouble offset,
702 gdouble x,
703 gdouble y)
704 {
705 if (dist == 0.0)
706 {
707 return 0.0;
708 }
709 else
710 {
711 gdouble r;
712 gdouble rat;
713
714 offset = offset / 100.0;
715
716 r = vec[0] * x + vec[1] * y;
717 rat = r / dist;
718
719 if (rat >= 0.0 && rat < offset)
720 return 0.0;
721 else if (offset == 1.0)
722 return (rat >= 1.0) ? 1.0 : 0.0;
723 else if (rat < 0.0)
724 return rat / (1.0 - offset);
725 else
726 return (rat - offset) / (1.0 - offset);
727 }
728 }
729
730 static gdouble
gradient_calc_bilinear_factor(gdouble dist,gdouble * vec,gdouble offset,gdouble x,gdouble y)731 gradient_calc_bilinear_factor (gdouble dist,
732 gdouble *vec,
733 gdouble offset,
734 gdouble x,
735 gdouble y)
736 {
737 if (dist == 0.0)
738 {
739 return 0.0;
740 }
741 else
742 {
743 gdouble r;
744 gdouble rat;
745
746 /* Calculate linear offset from the start line outward */
747
748 offset = offset / 100.0;
749
750 r = vec[0] * x + vec[1] * y;
751 rat = r / dist;
752
753 if (fabs (rat) < offset)
754 return 0.0;
755 else if (offset == 1.0)
756 return (rat == 1.0) ? 1.0 : 0.0;
757 else
758 return (fabs (rat) - offset) / (1.0 - offset);
759 }
760 }
761
762 static gdouble
gradient_calc_spiral_factor(gdouble dist,gdouble * axis,gdouble offset,gdouble x,gdouble y,gboolean clockwise)763 gradient_calc_spiral_factor (gdouble dist,
764 gdouble *axis,
765 gdouble offset,
766 gdouble x,
767 gdouble y,
768 gboolean clockwise)
769 {
770 if (dist == 0.0)
771 {
772 return 0.0;
773 }
774 else if (x != 0.0 || y != 0.0)
775 {
776 gdouble ang0, ang1;
777 gdouble ang;
778 double r;
779
780 offset = offset / 100.0;
781
782 ang0 = atan2 (axis[0], axis[1]) + G_PI;
783 ang1 = atan2 (x, y) + G_PI;
784
785 if (clockwise)
786 ang = ang1 - ang0;
787 else
788 ang = ang0 - ang1;
789
790 if (ang < 0.0)
791 ang += (2.0 * G_PI);
792
793 r = sqrt (SQR (x) + SQR (y)) / dist;
794
795 return fmod (ang / (2.0 * G_PI) + r + offset, 1.0);
796 }
797 else
798 {
799 return 0.5 ; /* We are on the middle point */
800 }
801 }
802
803 static gdouble
gradient_calc_shapeburst_angular_factor(GeglSampler * dist_sampler,gdouble offset,gdouble x,gdouble y)804 gradient_calc_shapeburst_angular_factor (GeglSampler *dist_sampler,
805 gdouble offset,
806 gdouble x,
807 gdouble y)
808 {
809 gfloat value;
810
811 offset = offset / 100.0;
812
813 gegl_sampler_get (dist_sampler, x, y, NULL, &value, GEGL_ABYSS_NONE);
814
815 value = 1.0 - value;
816
817 if (value < offset)
818 value = 0.0;
819 else if (offset == 1.0)
820 value = (value >= 1.0) ? 1.0 : 0.0;
821 else
822 value = (value - offset) / (1.0 - offset);
823
824 return value;
825 }
826
827
828 static gdouble
gradient_calc_shapeburst_spherical_factor(GeglSampler * dist_sampler,gdouble offset,gdouble x,gdouble y)829 gradient_calc_shapeburst_spherical_factor (GeglSampler *dist_sampler,
830 gdouble offset,
831 gdouble x,
832 gdouble y)
833 {
834 gfloat value;
835
836 offset = 1.0 - offset / 100.0;
837
838 gegl_sampler_get (dist_sampler, x, y, NULL, &value, GEGL_ABYSS_NONE);
839
840 if (value > offset)
841 value = 1.0;
842 else if (offset == 0.0)
843 value = (value <= 0.0) ? 0.0 : 1.0;
844 else
845 value = value / offset;
846
847 value = 1.0 - sin (0.5 * G_PI * value);
848
849 return value;
850 }
851
852
853 static gdouble
gradient_calc_shapeburst_dimpled_factor(GeglSampler * dist_sampler,gdouble offset,gdouble x,gdouble y)854 gradient_calc_shapeburst_dimpled_factor (GeglSampler *dist_sampler,
855 gdouble offset,
856 gdouble x,
857 gdouble y)
858 {
859 gfloat value;
860
861 offset = 1.0 - offset / 100.0;
862
863 gegl_sampler_get (dist_sampler, x, y, NULL, &value, GEGL_ABYSS_NONE);
864
865 if (value > offset)
866 value = 1.0;
867 else if (offset == 0.0)
868 value = (value <= 0.0) ? 0.0 : 1.0;
869 else
870 value = value / offset;
871
872 value = cos (0.5 * G_PI * value);
873
874 return value;
875 }
876
877 static void
gradient_render_pixel(gdouble x,gdouble y,GimpRGB * color,gpointer render_data)878 gradient_render_pixel (gdouble x,
879 gdouble y,
880 GimpRGB *color,
881 gpointer render_data)
882 {
883 RenderBlendData *rbd = render_data;
884 gdouble factor;
885
886 /* we want to calculate the color at the pixel's center */
887 x += 0.5;
888 y += 0.5;
889
890 /* Calculate blending factor */
891
892 switch (rbd->gradient_type)
893 {
894 case GIMP_GRADIENT_LINEAR:
895 factor = gradient_calc_linear_factor (rbd->dist,
896 rbd->vec, rbd->offset,
897 x - rbd->sx, y - rbd->sy);
898 break;
899
900 case GIMP_GRADIENT_BILINEAR:
901 factor = gradient_calc_bilinear_factor (rbd->dist,
902 rbd->vec, rbd->offset,
903 x - rbd->sx, y - rbd->sy);
904 break;
905
906 case GIMP_GRADIENT_RADIAL:
907 factor = gradient_calc_radial_factor (rbd->dist,
908 rbd->offset,
909 x - rbd->sx, y - rbd->sy);
910 break;
911
912 case GIMP_GRADIENT_SQUARE:
913 factor = gradient_calc_square_factor (rbd->dist, rbd->offset,
914 x - rbd->sx, y - rbd->sy);
915 break;
916
917 case GIMP_GRADIENT_CONICAL_SYMMETRIC:
918 factor = gradient_calc_conical_sym_factor (rbd->dist,
919 rbd->vec, rbd->offset,
920 x - rbd->sx, y - rbd->sy);
921 break;
922
923 case GIMP_GRADIENT_CONICAL_ASYMMETRIC:
924 factor = gradient_calc_conical_asym_factor (rbd->dist,
925 rbd->vec, rbd->offset,
926 x - rbd->sx, y - rbd->sy);
927 break;
928
929 case GIMP_GRADIENT_SHAPEBURST_ANGULAR:
930 factor = gradient_calc_shapeburst_angular_factor (rbd->dist_sampler,
931 rbd->offset,
932 x, y);
933 break;
934
935 case GIMP_GRADIENT_SHAPEBURST_SPHERICAL:
936 factor = gradient_calc_shapeburst_spherical_factor (rbd->dist_sampler,
937 rbd->offset,
938 x, y);
939 break;
940
941 case GIMP_GRADIENT_SHAPEBURST_DIMPLED:
942 factor = gradient_calc_shapeburst_dimpled_factor (rbd->dist_sampler,
943 rbd->offset,
944 x, y);
945 break;
946
947 case GIMP_GRADIENT_SPIRAL_CLOCKWISE:
948 factor = gradient_calc_spiral_factor (rbd->dist,
949 rbd->vec, rbd->offset,
950 x - rbd->sx, y - rbd->sy, TRUE);
951 break;
952
953 case GIMP_GRADIENT_SPIRAL_ANTICLOCKWISE:
954 factor = gradient_calc_spiral_factor (rbd->dist,
955 rbd->vec, rbd->offset,
956 x - rbd->sx, y - rbd->sy, FALSE);
957 break;
958
959 default:
960 g_return_if_reached ();
961 break;
962 }
963
964 /* Adjust for repeat */
965
966 switch (rbd->repeat)
967 {
968 case GIMP_REPEAT_NONE:
969 break;
970
971 case GIMP_REPEAT_SAWTOOTH:
972 factor = factor - floor (factor);
973 break;
974
975 case GIMP_REPEAT_TRIANGULAR:
976 {
977 guint ifactor;
978
979 if (factor < 0.0)
980 factor = -factor;
981
982 ifactor = (guint) factor;
983 factor = factor - floor (factor);
984
985 if (ifactor & 1)
986 factor = 1.0 - factor;
987 }
988 break;
989
990 case GIMP_REPEAT_TRUNCATE:
991 if (factor < 0.0 || factor > 1.0)
992 {
993 gimp_rgba_set (color, 0.0, 0.0, 0.0, 0.0);
994 return;
995 }
996 break;
997 }
998
999 /* Blend the colors */
1000
1001 if (rbd->gradient_cache)
1002 {
1003 factor = CLAMP (factor, 0.0, 1.0);
1004
1005 *color =
1006 rbd->gradient_cache[ROUND (factor * (rbd->gradient_cache_size - 1))];
1007 }
1008 else
1009 {
1010 rbd->last_seg = gimp_gradient_get_color_at (rbd->gradient, NULL,
1011 rbd->last_seg, factor,
1012 rbd->reverse,
1013 rbd->blend_color_space,
1014 color);
1015 }
1016 }
1017
1018 static void
gradient_put_pixel(gint x,gint y,GimpRGB * color,gpointer put_pixel_data)1019 gradient_put_pixel (gint x,
1020 gint y,
1021 GimpRGB *color,
1022 gpointer put_pixel_data)
1023 {
1024 PutPixelData *ppd = put_pixel_data;
1025 const gint index = (y - ppd->roi.y) * ppd->roi.width + (x - ppd->roi.x);
1026 gfloat *dest = ppd->data + 4 * index;
1027
1028 if (ppd->dither_rand)
1029 {
1030 gradient_dither_pixel (color, ppd->dither_rand, dest);
1031
1032 dest += 4;
1033 }
1034 else
1035 {
1036 *dest++ = color->r;
1037 *dest++ = color->g;
1038 *dest++ = color->b;
1039 *dest++ = color->a;
1040 }
1041 }
1042
1043 static void
gradient_dither_pixel(GimpRGB * color,GRand * dither_rand,gfloat * dest)1044 gradient_dither_pixel (GimpRGB *color,
1045 GRand *dither_rand,
1046 gfloat *dest)
1047 {
1048 gfloat r, g, b, a;
1049 guint i;
1050
1051 i = g_rand_int (dither_rand);
1052
1053 r = color->r + (gdouble) (i & 0xff) / 256.0 / 256.0 - 0.5 / 256.0; i >>= 8;
1054 g = color->g + (gdouble) (i & 0xff) / 256.0 / 256.0 - 0.5 / 256.0; i >>= 8;
1055 b = color->b + (gdouble) (i & 0xff) / 256.0 / 256.0 - 0.5 / 256.0; i >>= 8;
1056
1057 if (color->a > 0.0 && color->a < 1.0)
1058 a = color->a + (gdouble) (i & 0xff) / 256.0 / 256.0 - 0.5 / 256.0;
1059 else
1060 a = color->a;
1061
1062 *dest++ = CLAMP (r, 0.0, 1.0);
1063 *dest++ = CLAMP (g, 0.0, 1.0);
1064 *dest++ = CLAMP (b, 0.0, 1.0);
1065 *dest++ = CLAMP (a, 0.0, 1.0);
1066 }
1067
1068 static gboolean
gimp_operation_gradient_process(GeglOperation * operation,GeglBuffer * input,GeglBuffer * output,const GeglRectangle * result,gint level)1069 gimp_operation_gradient_process (GeglOperation *operation,
1070 GeglBuffer *input,
1071 GeglBuffer *output,
1072 const GeglRectangle *result,
1073 gint level)
1074 {
1075 GimpOperationGradient *self = GIMP_OPERATION_GRADIENT (operation);
1076
1077 const gdouble sx = self->start_x;
1078 const gdouble sy = self->start_y;
1079 const gdouble ex = self->end_x;
1080 const gdouble ey = self->end_y;
1081
1082 RenderBlendData rbd = { 0, };
1083
1084 GeglBufferIterator *iter;
1085 GeglRectangle *roi;
1086 GRand *dither_rand = NULL;
1087
1088 if (! self->gradient)
1089 return TRUE;
1090
1091 gimp_operation_gradient_validate_cache (self);
1092
1093 rbd.gradient = self->gradient;
1094 rbd.reverse = self->gradient_reverse;
1095 rbd.blend_color_space = self->gradient_blend_color_space;
1096 rbd.gradient_cache = self->gradient_cache;
1097 rbd.gradient_cache_size = self->gradient_cache_size;
1098
1099 /* Calculate type-specific parameters */
1100
1101 switch (self->gradient_type)
1102 {
1103 case GIMP_GRADIENT_RADIAL:
1104 rbd.dist = sqrt (SQR (ex - sx) + SQR (ey - sy));
1105 break;
1106
1107 case GIMP_GRADIENT_SQUARE:
1108 rbd.dist = MAX (fabs (ex - sx), fabs (ey - sy));
1109 break;
1110
1111 case GIMP_GRADIENT_CONICAL_SYMMETRIC:
1112 case GIMP_GRADIENT_CONICAL_ASYMMETRIC:
1113 case GIMP_GRADIENT_SPIRAL_CLOCKWISE:
1114 case GIMP_GRADIENT_SPIRAL_ANTICLOCKWISE:
1115 case GIMP_GRADIENT_LINEAR:
1116 case GIMP_GRADIENT_BILINEAR:
1117 rbd.dist = sqrt (SQR (ex - sx) + SQR (ey - sy));
1118
1119 if (rbd.dist > 0.0)
1120 {
1121 rbd.vec[0] = (ex - sx) / rbd.dist;
1122 rbd.vec[1] = (ey - sy) / rbd.dist;
1123 }
1124
1125 break;
1126
1127 case GIMP_GRADIENT_SHAPEBURST_ANGULAR:
1128 case GIMP_GRADIENT_SHAPEBURST_SPHERICAL:
1129 case GIMP_GRADIENT_SHAPEBURST_DIMPLED:
1130 rbd.dist = sqrt (SQR (ex - sx) + SQR (ey - sy));
1131 rbd.dist_sampler = gegl_buffer_sampler_new_at_level (
1132 input, babl_format ("Y float"), GEGL_SAMPLER_NEAREST, level);
1133 break;
1134
1135 default:
1136 g_return_val_if_reached (FALSE);
1137 break;
1138 }
1139
1140 /* Initialize render data */
1141
1142 rbd.offset = self->offset;
1143 rbd.sx = self->start_x;
1144 rbd.sy = self->start_y;
1145 rbd.gradient_type = self->gradient_type;
1146 rbd.repeat = self->gradient_repeat;
1147
1148 /* Render the gradient! */
1149
1150 iter = gegl_buffer_iterator_new (output, result, 0,
1151 babl_format ("R'G'B'A float"),
1152 GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
1153 roi = &iter->items[0].roi;
1154
1155 if (self->dither)
1156 dither_rand = g_rand_new ();
1157
1158 if (self->supersample)
1159 {
1160 PutPixelData ppd;
1161
1162 ppd.dither_rand = dither_rand;
1163
1164 while (gegl_buffer_iterator_next (iter))
1165 {
1166 ppd.data = iter->items[0].data;
1167 ppd.roi = *roi;
1168
1169 gimp_adaptive_supersample_area (roi->x, roi->y,
1170 roi->x + roi->width - 1,
1171 roi->y + roi->height - 1,
1172 self->supersample_depth,
1173 self->supersample_threshold,
1174 gradient_render_pixel, &rbd,
1175 gradient_put_pixel, &ppd,
1176 NULL,
1177 NULL);
1178 }
1179 }
1180 else
1181 {
1182 while (gegl_buffer_iterator_next (iter))
1183 {
1184 gfloat *dest = iter->items[0].data;
1185 gint endx = roi->x + roi->width;
1186 gint endy = roi->y + roi->height;
1187 gint x, y;
1188
1189 if (dither_rand)
1190 {
1191 for (y = roi->y; y < endy; y++)
1192 for (x = roi->x; x < endx; x++)
1193 {
1194 GimpRGB color = { 0.0, 0.0, 0.0, 1.0 };
1195
1196 gradient_render_pixel (x, y, &color, &rbd);
1197 gradient_dither_pixel (&color, dither_rand, dest);
1198
1199 dest += 4;
1200 }
1201 }
1202 else
1203 {
1204 for (y = roi->y; y < endy; y++)
1205 for (x = roi->x; x < endx; x++)
1206 {
1207 GimpRGB color = { 0.0, 0.0, 0.0, 1.0 };
1208
1209 gradient_render_pixel (x, y, &color, &rbd);
1210
1211 *dest++ = color.r;
1212 *dest++ = color.g;
1213 *dest++ = color.b;
1214 *dest++ = color.a;
1215 }
1216 }
1217 }
1218 }
1219
1220 if (self->dither)
1221 g_rand_free (dither_rand);
1222
1223 g_clear_object (&rbd.dist_sampler);
1224
1225 return TRUE;
1226 }
1227
1228 static void
gimp_operation_gradient_invalidate_cache(GimpOperationGradient * self)1229 gimp_operation_gradient_invalidate_cache (GimpOperationGradient *self)
1230 {
1231 g_clear_pointer (&self->gradient_cache, g_free);
1232 }
1233
1234 static void
gimp_operation_gradient_validate_cache(GimpOperationGradient * self)1235 gimp_operation_gradient_validate_cache (GimpOperationGradient *self)
1236 {
1237 GimpGradientSegment *last_seg = NULL;
1238 gint cache_size;
1239 gint i;
1240
1241 if (! self->gradient)
1242 return;
1243
1244 g_mutex_lock (&self->gradient_cache_mutex);
1245
1246 if (self->gradient_cache)
1247 {
1248 g_mutex_unlock (&self->gradient_cache_mutex);
1249
1250 return;
1251 }
1252
1253 cache_size = ceil (hypot (self->start_x - self->end_x,
1254 self->start_y - self->end_y)) *
1255 GRADIENT_CACHE_N_SUPERSAMPLES;
1256
1257 /* have at least two values in the cache */
1258 cache_size = MAX (cache_size, 2);
1259
1260 /* don't use a cache if its necessary size is too big */
1261 if (cache_size > GRADIENT_CACHE_MAX_SIZE)
1262 {
1263 g_mutex_unlock (&self->gradient_cache_mutex);
1264
1265 return;
1266 }
1267
1268 self->gradient_cache = g_new0 (GimpRGB, cache_size);
1269 self->gradient_cache_size = cache_size;
1270
1271 for (i = 0; i < self->gradient_cache_size; i++)
1272 {
1273 gdouble factor = (gdouble) i / (gdouble) (self->gradient_cache_size - 1);
1274
1275 last_seg = gimp_gradient_get_color_at (self->gradient, NULL, last_seg,
1276 factor,
1277 self->gradient_reverse,
1278 self->gradient_blend_color_space,
1279 self->gradient_cache + i);
1280 }
1281
1282 g_mutex_unlock (&self->gradient_cache_mutex);
1283 }
1284