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