1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #include <stdlib.h>
21
22 #include <gegl.h>
23 #include <gtk/gtk.h>
24
25 #include "libgimpmath/gimpmath.h"
26
27 #include "tools-types.h"
28
29 #include "gegl/gimp-gegl-loops.h"
30
31 #include "core/gimppickable.h"
32
33 #include "gimptilehandleriscissors.h"
34
35
36 enum
37 {
38 PROP_0,
39 PROP_PICKABLE
40 };
41
42
43 static void gimp_tile_handler_iscissors_finalize (GObject *object);
44 static void gimp_tile_handler_iscissors_set_property (GObject *object,
45 guint property_id,
46 const GValue *value,
47 GParamSpec *pspec);
48 static void gimp_tile_handler_iscissors_get_property (GObject *object,
49 guint property_id,
50 GValue *value,
51 GParamSpec *pspec);
52
53 static void gimp_tile_handler_iscissors_validate (GimpTileHandlerValidate *validate,
54 const GeglRectangle *rect,
55 const Babl *format,
56 gpointer dest_buf,
57 gint dest_stride);
58
59
G_DEFINE_TYPE(GimpTileHandlerIscissors,gimp_tile_handler_iscissors,GIMP_TYPE_TILE_HANDLER_VALIDATE)60 G_DEFINE_TYPE (GimpTileHandlerIscissors, gimp_tile_handler_iscissors,
61 GIMP_TYPE_TILE_HANDLER_VALIDATE)
62
63 #define parent_class gimp_tile_handler_iscissors_parent_class
64
65
66 static void
67 gimp_tile_handler_iscissors_class_init (GimpTileHandlerIscissorsClass *klass)
68 {
69 GObjectClass *object_class = G_OBJECT_CLASS (klass);
70 GimpTileHandlerValidateClass *validate_class;
71
72 validate_class = GIMP_TILE_HANDLER_VALIDATE_CLASS (klass);
73
74 object_class->finalize = gimp_tile_handler_iscissors_finalize;
75 object_class->set_property = gimp_tile_handler_iscissors_set_property;
76 object_class->get_property = gimp_tile_handler_iscissors_get_property;
77
78 validate_class->validate = gimp_tile_handler_iscissors_validate;
79
80 g_object_class_install_property (object_class, PROP_PICKABLE,
81 g_param_spec_object ("pickable", NULL, NULL,
82 GIMP_TYPE_PICKABLE,
83 GIMP_PARAM_READWRITE |
84 G_PARAM_CONSTRUCT_ONLY));
85 }
86
87 static void
gimp_tile_handler_iscissors_init(GimpTileHandlerIscissors * iscissors)88 gimp_tile_handler_iscissors_init (GimpTileHandlerIscissors *iscissors)
89 {
90 }
91
92 static void
gimp_tile_handler_iscissors_finalize(GObject * object)93 gimp_tile_handler_iscissors_finalize (GObject *object)
94 {
95 GimpTileHandlerIscissors *iscissors = GIMP_TILE_HANDLER_ISCISSORS (object);
96
97 if (iscissors->pickable)
98 {
99 g_object_unref (iscissors->pickable);
100 iscissors->pickable = NULL;
101 }
102
103 G_OBJECT_CLASS (parent_class)->finalize (object);
104 }
105
106 static void
gimp_tile_handler_iscissors_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)107 gimp_tile_handler_iscissors_set_property (GObject *object,
108 guint property_id,
109 const GValue *value,
110 GParamSpec *pspec)
111 {
112 GimpTileHandlerIscissors *iscissors = GIMP_TILE_HANDLER_ISCISSORS (object);
113
114 switch (property_id)
115 {
116 case PROP_PICKABLE:
117 iscissors->pickable = g_value_dup_object (value);
118 break;
119
120 default:
121 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
122 break;
123 }
124 }
125
126 static void
gimp_tile_handler_iscissors_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)127 gimp_tile_handler_iscissors_get_property (GObject *object,
128 guint property_id,
129 GValue *value,
130 GParamSpec *pspec)
131 {
132 GimpTileHandlerIscissors *iscissors = GIMP_TILE_HANDLER_ISCISSORS (object);
133
134 switch (property_id)
135 {
136 case PROP_PICKABLE:
137 g_value_set_object (value, iscissors->pickable);
138 break;
139
140 default:
141 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
142 break;
143 }
144 }
145
146 static const gfloat horz_deriv[9] =
147 {
148 1, 0, -1,
149 2, 0, -2,
150 1, 0, -1,
151 };
152
153 static const gfloat vert_deriv[9] =
154 {
155 1, 2, 1,
156 0, 0, 0,
157 -1, -2, -1,
158 };
159
160 static const gfloat blur_32[9] =
161 {
162 1, 1, 1,
163 1, 24, 1,
164 1, 1, 1,
165 };
166
167 #define MAX_GRADIENT 179.606 /* == sqrt (127^2 + 127^2) */
168 #define MIN_GRADIENT 63 /* gradients < this are directionless */
169 #define COST_WIDTH 2 /* number of bytes for each pixel in cost map */
170
171 static void
gimp_tile_handler_iscissors_validate(GimpTileHandlerValidate * validate,const GeglRectangle * rect,const Babl * format,gpointer dest_buf,gint dest_stride)172 gimp_tile_handler_iscissors_validate (GimpTileHandlerValidate *validate,
173 const GeglRectangle *rect,
174 const Babl *format,
175 gpointer dest_buf,
176 gint dest_stride)
177 {
178 GimpTileHandlerIscissors *iscissors = GIMP_TILE_HANDLER_ISCISSORS (validate);
179 GeglBuffer *src;
180 GeglBuffer *temp0;
181 GeglBuffer *temp1;
182 GeglBuffer *temp2;
183 gint stride1;
184 gint stride2;
185 gint i, j;
186
187 /* temporary convolution buffers -- */
188 guchar *maxgrad_conv1;
189 guchar *maxgrad_conv2;
190
191 #if 0
192 g_printerr ("validating at %d %d %d %d\n",
193 rect->x,
194 rect->y,
195 rect->width,
196 rect->height);
197 #endif
198
199 gimp_pickable_flush (iscissors->pickable);
200
201 src = gimp_pickable_get_buffer (iscissors->pickable);
202
203 temp0 = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
204 rect->width,
205 rect->height),
206 babl_format ("R'G'B'A u8"));
207
208 temp1 = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
209 rect->width,
210 rect->height),
211 babl_format ("R'G'B'A u8"));
212
213 temp2 = gegl_buffer_new (GEGL_RECTANGLE (0, 0,
214 rect->width,
215 rect->height),
216 babl_format ("R'G'B'A u8"));
217
218 /* XXX tile edges? */
219
220 /* Blur the source to get rid of noise */
221 gimp_gegl_convolve (src, rect,
222 temp0, GEGL_RECTANGLE (0, 0, rect->width, rect->height),
223 blur_32, 3, 32, GIMP_NORMAL_CONVOL, FALSE);
224
225 /* Use this blurred region as the new source */
226
227 /* Get the horizontal derivative */
228 gimp_gegl_convolve (temp0, GEGL_RECTANGLE (0, 0, rect->width, rect->height),
229 temp1, GEGL_RECTANGLE (0, 0, rect->width, rect->height),
230 horz_deriv, 3, 1, GIMP_NEGATIVE_CONVOL, FALSE);
231
232 /* Get the vertical derivative */
233 gimp_gegl_convolve (temp0, GEGL_RECTANGLE (0, 0, rect->width, rect->height),
234 temp2, GEGL_RECTANGLE (0, 0, rect->width, rect->height),
235 vert_deriv, 3, 1, GIMP_NEGATIVE_CONVOL, FALSE);
236
237 maxgrad_conv1 =
238 (guchar *) gegl_buffer_linear_open (temp1,
239 GEGL_RECTANGLE (0, 0,
240 rect->width,
241 rect->height),
242 &stride1, NULL);
243
244 maxgrad_conv2 =
245 (guchar *) gegl_buffer_linear_open (temp2,
246 GEGL_RECTANGLE (0, 0,
247 rect->width,
248 rect->height),
249 &stride2, NULL);
250
251 /* calculate overall gradient */
252
253 for (i = 0; i < rect->height; i++)
254 {
255 const guint8 *datah = maxgrad_conv1 + stride1 * i;
256 const guint8 *datav = maxgrad_conv2 + stride2 * i;
257 guint8 *gradmap = (guint8 *) dest_buf + dest_stride * i;
258
259 for (j = 0; j < rect->width; j++)
260 {
261 gint8 hmax = datah[0] - 128;
262 gint8 vmax = datav[0] - 128;
263 gfloat gradient;
264 gint b;
265
266 for (b = 1; b < 4; b++)
267 {
268 if (abs (datah[b] - 128) > abs (hmax))
269 hmax = datah[b] - 128;
270
271 if (abs (datav[b] - 128) > abs (vmax))
272 vmax = datav[b] - 128;
273 }
274
275 if (i == 0 || j == 0 || i == rect->height - 1 || j == rect->width - 1)
276 {
277 gradmap[j * COST_WIDTH + 0] = 0;
278 gradmap[j * COST_WIDTH + 1] = 255;
279 goto contin;
280 }
281
282 /* 1 byte absolute magnitude first */
283 gradient = sqrt (SQR (hmax) + SQR (vmax));
284 gradmap[j * COST_WIDTH] = gradient * 255 / MAX_GRADIENT;
285
286 /* then 1 byte direction */
287 if (gradient > MIN_GRADIENT)
288 {
289 gfloat direction;
290
291 if (! hmax)
292 direction = (vmax > 0) ? G_PI_2 : -G_PI_2;
293 else
294 direction = atan ((gdouble) vmax / (gdouble) hmax);
295
296 /* Scale the direction from between 0 and 254,
297 * corresponding to -PI/2, PI/2 255 is reserved for
298 * directionless pixels
299 */
300 gradmap[j * COST_WIDTH + 1] =
301 (guint8) (254 * (direction + G_PI_2) / G_PI);
302 }
303 else
304 {
305 gradmap[j * COST_WIDTH + 1] = 255; /* reserved for weak gradient */
306 }
307
308 contin:
309 datah += 4;
310 datav += 4;
311 }
312 }
313
314 gegl_buffer_linear_close (temp1, maxgrad_conv1);
315 gegl_buffer_linear_close (temp2, maxgrad_conv2);
316
317 g_object_unref (temp0);
318 g_object_unref (temp1);
319 g_object_unref (temp2);
320 }
321
322 GeglTileHandler *
gimp_tile_handler_iscissors_new(GimpPickable * pickable)323 gimp_tile_handler_iscissors_new (GimpPickable *pickable)
324 {
325 g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL);
326
327 return g_object_new (GIMP_TYPE_TILE_HANDLER_ISCISSORS,
328 "whole-tile", TRUE,
329 "pickable", pickable,
330 NULL);
331 }
332