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