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 
22 /* #define MANUAL_CONTROL */
23 
24 #define MAX_GAMMA 1000.0
25 
26 #ifdef GEGL_PROPERTIES
27 
28 enum_start (gegl_focus_blur_type)
29   enum_value (GEGL_FOCUS_BLUR_TYPE_GAUSSIAN, "gaussian", N_("Gaussian Blur"))
30   enum_value (GEGL_FOCUS_BLUR_TYPE_LENS,     "lens",     N_("Lens Blur"))
31 enum_end (GeglFocusBlurType)
32 
33 enum_start (gegl_focus_blur_shape)
34   enum_value (GEGL_FOCUS_BLUR_SHAPE_CIRCLE,     "circle",     N_("Circle"))
35   enum_value (GEGL_FOCUS_BLUR_SHAPE_SQUARE,     "square",     N_("Square"))
36   enum_value (GEGL_FOCUS_BLUR_SHAPE_DIAMOND,    "diamond",    N_("Diamond"))
37   enum_value (GEGL_FOCUS_BLUR_SHAPE_HORIZONTAL, "horizontal", N_("Horizontal"))
38   enum_value (GEGL_FOCUS_BLUR_SHAPE_VERTICAL,   "vertical",   N_("Vertical"))
39 enum_end (GeglFocusBlurShape)
40 
41 property_enum (blur_type, _("Blur type"),
42                GeglFocusBlurType, gegl_focus_blur_type,
43                GEGL_FOCUS_BLUR_TYPE_GAUSSIAN)
44 
45 property_double (blur_radius, _("Blur radius"), 25.0)
46     description (_("Out-of-focus blur radius"))
47     value_range (0.0, 1500.0)
48     ui_range    (0.0, 100.0)
49     ui_gamma    (2.0)
50     ui_meta     ("unit", "pixel-distance")
51 
52 property_double (highlight_factor, _("Highlight factor"), 0.0)
53     description (_("Relative highlight strength"))
54     value_range (0.0, 1.0)
55     ui_meta     ("visible", "blur-type {lens}")
56 
57 property_double (highlight_threshold_low, _("Highlight threshold (low)"), 0.0)
58     ui_range    (0.0, 1.0)
59     ui_meta     ("role", "range-start")
60     ui_meta     ("unit", "luminance")
61     ui_meta     ("range-label", _("Highlight threshold"))
62     ui_meta     ("visible", "$highlight-factor.visible")
63 
64 property_double (highlight_threshold_high, _("Highlight threshold (high)"), 1.0)
65     ui_range    (0.0, 1.0)
66     ui_meta     ("role", "range-end")
67     ui_meta     ("unit", "luminance")
68     ui_meta     ("visible", "$highlight-threshold-low.visible")
69 
70 property_enum (shape, _("Shape"),
71                GeglFocusBlurShape,
72                gegl_focus_blur_shape,
73                GEGL_FOCUS_BLUR_SHAPE_CIRCLE)
74 
75 property_double (x, _("Center X"), 0.5)
76     ui_range    (0, 1.0)
77     ui_meta     ("unit", "relative-coordinate")
78     ui_meta     ("axis", "x")
79 
80 property_double (y, _("Center Y"), 0.5)
81     ui_range    (0, 1.0)
82     ui_meta     ("unit", "relative-coordinate")
83     ui_meta     ("axis", "y")
84 
85 property_double (radius, _("Radius"), 0.75)
86     description (_("Focus-region outer radius"))
87     value_range (0.0, G_MAXDOUBLE)
88     ui_range    (0.0, 5.0)
89     ui_meta     ("unit", "relative-distance")
90 
91 property_double (focus, _("Sharpness"), 0.25)
92     description (_("Focus-region inner limit"))
93     value_range (0.0, 1.0)
94 
95 property_double (midpoint, _("Midpoint"), 0.5)
96     description (_("Focus-transition midpoint"))
97     value_range (0.0, 1.0)
98 
99 property_double (aspect_ratio, _("Aspect ratio"), 0.0)
100     value_range (-1.0, +1.0)
101 
102 property_double (rotation, _("Rotation"), 0.0)
103     value_range (-180.0, +180.0)
104     ui_meta     ("unit", "degree")
105     ui_meta     ("direction", "cw")
106 
107 #ifdef MANUAL_CONTROL
108 
109 property_int (blur_levels, _("Blur levels"), 8)
110     description (_("Number of blur levels"))
111     value_range (2, 16)
112     ui_meta     ("visible", "blur-type {gaussian}")
113 
114 property_double (blur_gamma, _("Blur gamma"), 1.5)
115     description (_("Gamma factor for blur-level spacing"))
116     value_range (0.0, G_MAXDOUBLE)
117     ui_range    (0.1, 10.0)
118     ui_meta     ("visible", "blur-type {gaussian}")
119 
120 #else
121 
122 property_boolean (high_quality, _("High quality"), FALSE)
123     description (_("Generate more accurate and consistent output (slower)"))
124     ui_meta     ("visible", "blur-type {gaussian}")
125 
126 #endif
127 
128 #else
129 
130 #define GEGL_OP_META
131 #define GEGL_OP_NAME     focus_blur
132 #define GEGL_OP_C_SOURCE focus-blur.c
133 
134 #include "gegl-op.h"
135 
136 typedef struct
137 {
138   GeglFocusBlurType  blur_type;
139 
140   GeglNode          *input;
141   GeglNode          *output;
142 
143   GeglNode          *color;
144   GeglNode          *crop;
145   GeglNode          *vignette;
146 
147   GeglNode          *blur;
148 } Nodes;
149 
150 static void
151 update (GeglOperation *operation)
152 {
153   GeglProperties *o     = GEGL_PROPERTIES (operation);
154   Nodes          *nodes = o->user_data;
155 
156   gdouble scale;
157   gdouble squeeze;
158 
159   if (o->aspect_ratio >= 0.0)
160     scale = 1.0 - o->aspect_ratio;
161   else
162     scale = 1.0 / (1.0 + o->aspect_ratio);
163 
164   if (scale <= 1.0)
165     squeeze = +2.0 * atan (1.0 / scale - 1.0) / G_PI;
166   else
167     squeeze = -2.0 * atan (scale - 1.0) / G_PI;
168 
169   gegl_node_set (nodes->vignette,
170                  "shape",    o->shape,
171                  "radius",   o->radius,
172                  "softness", 1.0 - o->focus,
173                  "gamma",    o->midpoint < 1.0 ?
174                                MIN (log (0.5) / log (o->midpoint),
175                                     MAX_GAMMA) :
176                                MAX_GAMMA,
177                  "squeeze",  squeeze,
178                  "x",        o->x,
179                  "y",        o->y,
180                  "rotation", fmod (o->rotation + 360.0, 360.0),
181                  NULL);
182 
183   if (o->blur_type != nodes->blur_type)
184     {
185       nodes->blur_type = o->blur_type;
186 
187       switch (nodes->blur_type)
188         {
189         case GEGL_FOCUS_BLUR_TYPE_GAUSSIAN:
190           gegl_node_set (nodes->blur,
191                          "operation",   "gegl:variable-blur",
192                          "linear-mask", TRUE,
193                          NULL);
194 
195           gegl_operation_meta_redirect (operation,   "blur-radius",
196                                         nodes->blur, "radius");
197 #ifdef MANUAL_CONTROLS
198           gegl_operation_meta_redirect (operation,   "blur-levels",
199                                         nodes->blur, "levels");
200           gegl_operation_meta_redirect (operation,   "blur-gamma",
201                                         nodes->blur, "gamma");
202 #else
203           gegl_operation_meta_redirect (operation,   "high-quality",
204                                         nodes->blur, "high-quality");
205 #endif
206           break;
207 
208         case GEGL_FOCUS_BLUR_TYPE_LENS:
209           gegl_node_set (nodes->blur,
210                          "operation",   "gegl:lens-blur",
211                          "linear-mask", TRUE,
212                          NULL);
213 
214           gegl_operation_meta_redirect (operation,   "blur-radius",
215                                         nodes->blur, "radius");
216           gegl_operation_meta_redirect (operation,   "highlight-factor",
217                                         nodes->blur, "highlight-factor");
218           gegl_operation_meta_redirect (operation,   "highlight-threshold-low",
219                                         nodes->blur, "highlight-threshold-low");
220           gegl_operation_meta_redirect (operation,   "highlight-threshold-high",
221                                         nodes->blur, "highlight-threshold-high");
222           break;
223         }
224     }
225 }
226 
227 static void
228 attach (GeglOperation *operation)
229 {
230   GeglProperties *o     = GEGL_PROPERTIES (operation);
231   Nodes          *nodes;
232   GeglColor      *black = gegl_color_new ("black");
233   GeglColor      *white = gegl_color_new ("white");
234 
235   if (! o->user_data)
236     o->user_data = g_slice_new (Nodes);
237 
238   nodes = o->user_data;
239 
240   nodes->blur_type = -1;
241 
242   nodes->input  = gegl_node_get_input_proxy  (operation->node, "input");
243   nodes->output = gegl_node_get_output_proxy (operation->node, "output");
244 
245   nodes->color = gegl_node_new_child (
246     operation->node,
247     "operation", "gegl:color",
248     "value",     black,
249     NULL);
250 
251   nodes->crop = gegl_node_new_child (
252     operation->node,
253     "operation", "gegl:crop",
254     NULL);
255 
256   nodes->vignette = gegl_node_new_child (
257     operation->node,
258     "operation",  "gegl:vignette",
259     "color",      white,
260     "proportion", 0.0,
261     NULL);
262 
263   nodes->blur = gegl_node_new_child (
264     operation->node,
265     "operation", "gegl:variable-blur",
266     NULL);
267 
268   gegl_node_link_many (nodes->input,
269                        nodes->blur,
270                        nodes->output,
271                        NULL);
272 
273   gegl_node_link_many (nodes->color,
274                        nodes->crop,
275                        nodes->vignette,
276                        NULL);
277 
278   gegl_node_connect_to (nodes->input, "output",
279                         nodes->crop,  "aux");
280 
281   gegl_node_connect_to (nodes->vignette, "output",
282                         nodes->blur,     "aux");
283 
284   g_object_unref (white);
285   g_object_unref (black);
286 }
287 
288 static void
289 dispose (GObject *object)
290 {
291   GeglProperties *o = GEGL_PROPERTIES (object);
292 
293   if (o->user_data)
294     {
295       g_slice_free (Nodes, o->user_data);
296 
297       o->user_data = NULL;
298     }
299 
300   G_OBJECT_CLASS (gegl_op_parent_class)->dispose (object);
301 }
302 
303 static void
304 gegl_op_class_init (GeglOpClass *klass)
305 {
306   GObjectClass           *object_class;
307   GeglOperationClass     *operation_class;
308   GeglOperationMetaClass *operation_meta_class;
309 
310   object_class         = G_OBJECT_CLASS (klass);
311   operation_class      = GEGL_OPERATION_CLASS (klass);
312   operation_meta_class = GEGL_OPERATION_META_CLASS (klass);
313 
314   object_class->dispose        = dispose;
315   operation_class->attach      = attach;
316   operation_meta_class->update = update;
317 
318   gegl_operation_class_set_keys (operation_class,
319     "name",        "gegl:focus-blur",
320     "title",       _("Focus Blur"),
321     "categories",  "blur",
322     "reference-hash", "a6f7a6425769c7d8b1d277a5c3f25973",
323     "description", _("Blur the image around a focal point"),
324     NULL);
325 }
326 
327 #endif
328