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