1 /* This file is an image processing operation for GEGL
2  *
3  * GEGL is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 3 of the License, or (at your option) any later version.
7  *
8  * GEGL is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15  *
16  * Copyright 2020 Ell
17  */
18 
19 #include "config.h"
20 #include <glib/gi18n-lib.h>
21 #include <stdio.h>
22 
23 /* #define MANUAL_CONTROL */
24 
25 #define MAX_LEVELS 16
26 #define GAMMA      1.5
27 
28 #ifdef GEGL_PROPERTIES
29 
30 property_double (radius, _("Radius"), 10.0)
31     description (_("Maximal blur radius"))
32     value_range (0.0, 1500.0)
33     ui_range    (0.0, 100.0)
34     ui_gamma    (2.0)
35     ui_meta     ("unit", "pixel-distance")
36 
37 property_boolean (linear_mask, _("Linear mask"), FALSE)
38     description (_("Use linear mask values"))
39 
40 #ifdef MANUAL_CONTROL
41 
42 property_int (levels, _("Blur levels"), 8)
43     description (_("Number of blur levels"))
44     value_range (2, MAX_LEVELS)
45 
46 property_double (gamma, _("Blur gamma"), GAMMA)
47     description (_("Gamma factor for blur-level spacing"))
48     value_range (0.0, G_MAXDOUBLE)
49     ui_range    (0.1, 10.0)
50 
51 #else
52 
53 property_boolean (high_quality, _("High quality"), FALSE)
54     description (_("Generate more accurate and consistent output (slower)"))
55 
56 #endif
57 
58 #else
59 
60 #define GEGL_OP_META
61 #define GEGL_OP_NAME     variable_blur
62 #define GEGL_OP_C_SOURCE variable-blur.c
63 
64 #include "gegl-op.h"
65 
66 typedef struct
67 {
68   GeglNode *input;
69   GeglNode *aux;
70   GeglNode *output;
71 
72   GeglNode *gaussian_blur[MAX_LEVELS];
73 
74   GeglNode *piecewise_blend;
75 } Nodes;
76 
77 static void
78 update (GeglOperation *operation)
79 {
80   GeglProperties *o     = GEGL_PROPERTIES (operation);
81   Nodes          *nodes = o->user_data;
82 
83   gdouble gamma;
84   gint    levels;
85   gint    i;
86 
87 #ifdef MANUAL_CONTROL
88   levels = o->levels;
89   gamma  = o->gamma;
90 #else
91   if (o->high_quality)
92     {
93       levels = MAX_LEVELS;
94     }
95   else
96     {
97       levels = ceil (CLAMP (log (o->radius) / G_LN2 + 3,
98                             2, MAX_LEVELS));
99     }
100 
101   gamma = GAMMA;
102 #endif
103 
104   gegl_node_set (nodes->piecewise_blend,
105                  "levels", levels,
106                  "gamma",  gamma,
107                  NULL);
108 
109   for (i = 1; i < levels; i++)
110     {
111       gdouble radius;
112 
113       gegl_node_link (nodes->input, nodes->gaussian_blur[i]);
114 
115       radius = o->radius * pow ((gdouble) i / (levels - 1), gamma);
116 
117       gegl_node_set (nodes->gaussian_blur[i],
118                      "std-dev-x", radius,
119                      "std-dev-y", radius,
120                      NULL);
121     }
122 
123   for (; i < MAX_LEVELS; i++)
124     gegl_node_disconnect (nodes->gaussian_blur[i], "input");
125 }
126 
127 static void
128 attach (GeglOperation *operation)
129 {
130   GeglProperties *o = GEGL_PROPERTIES (operation);
131   Nodes          *nodes;
132   gint            i;
133 
134   if (! o->user_data)
135     o->user_data = g_slice_new (Nodes);
136 
137   nodes = o->user_data;
138 
139   nodes->input  = gegl_node_get_input_proxy  (operation->node, "input");
140   nodes->aux    = gegl_node_get_input_proxy  (operation->node, "aux");
141   nodes->output = gegl_node_get_output_proxy (operation->node, "output");
142 
143   nodes->piecewise_blend = gegl_node_new_child (
144     operation->node,
145     "operation", "gegl:piecewise-blend",
146     NULL);
147 
148   gegl_operation_meta_redirect (operation,              "linear-mask",
149                                 nodes->piecewise_blend, "linear-mask");
150 
151   gegl_node_connect_to (nodes->input,           "output",
152                         nodes->piecewise_blend, "aux1");
153 
154   for (i = 1; i < MAX_LEVELS; i++)
155     {
156       gchar aux_name[32];
157 
158       nodes->gaussian_blur[i] = gegl_node_new_child (
159         operation->node,
160         "operation", "gegl:gaussian-blur",
161         NULL);
162 
163       sprintf (aux_name, "aux%d", i + 1);
164 
165       gegl_node_connect_to (nodes->gaussian_blur[i], "output",
166                             nodes->piecewise_blend,  aux_name);
167     }
168 
169   gegl_node_link_many (nodes->aux,
170                        nodes->piecewise_blend,
171                        nodes->output,
172                        NULL);
173 }
174 
175 static void
176 dispose (GObject *object)
177 {
178   GeglProperties *o = GEGL_PROPERTIES (object);
179 
180   if (o->user_data)
181     {
182       g_slice_free (Nodes, o->user_data);
183 
184       o->user_data = NULL;
185     }
186 
187   G_OBJECT_CLASS (gegl_op_parent_class)->dispose (object);
188 }
189 
190 static void
191 gegl_op_class_init (GeglOpClass *klass)
192 {
193   GObjectClass           *object_class;
194   GeglOperationClass     *operation_class;
195   GeglOperationMetaClass *operation_meta_class;
196 
197   object_class         = G_OBJECT_CLASS (klass);
198   operation_class      = GEGL_OPERATION_CLASS (klass);
199   operation_meta_class = GEGL_OPERATION_META_CLASS (klass);
200 
201   object_class->dispose        = dispose;
202   operation_class->attach      = attach;
203   operation_meta_class->update = update;
204 
205   gegl_operation_class_set_keys (operation_class,
206     "name",           "gegl:variable-blur",
207     "title",          _("Variable Blur"),
208     "categories",     "blur",
209     "reference-hash", "553023d2b937e2ebeb216a7999dd12b3",
210     "description", _("Blur the image by a varying amount using a mask"),
211     NULL);
212 }
213 
214 #endif
215