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 <gegl.h>
21 
22 #include "gimp-gegl-types.h"
23 
24 #include "gegl/gimp-gegl-mask.h"
25 
26 
27 gboolean
gimp_gegl_mask_bounds(GeglBuffer * buffer,gint * x1,gint * y1,gint * x2,gint * y2)28 gimp_gegl_mask_bounds (GeglBuffer *buffer,
29                        gint        *x1,
30                        gint        *y1,
31                        gint        *x2,
32                        gint        *y2)
33 {
34   GeglBufferIterator  *iter;
35   const GeglRectangle *extent;
36   const GeglRectangle *roi;
37   const Babl          *format;
38   gint                 bpp;
39   gint                 tx1, tx2, ty1, ty2;
40 
41   g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE);
42   g_return_val_if_fail (x1 != NULL, FALSE);
43   g_return_val_if_fail (y1 != NULL, FALSE);
44   g_return_val_if_fail (x2 != NULL, FALSE);
45   g_return_val_if_fail (y2 != NULL, FALSE);
46 
47   extent = gegl_buffer_get_extent (buffer);
48 
49   /*  go through and calculate the bounds  */
50   tx1 = extent->x + extent->width;
51   ty1 = extent->y + extent->height;
52   tx2 = extent->x;
53   ty2 = extent->y;
54 
55   format = gegl_buffer_get_format (buffer);
56   bpp    = babl_format_get_bytes_per_pixel (format);
57 
58   iter = gegl_buffer_iterator_new (buffer, NULL, 0, format,
59                                    GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
60   roi = &iter->items[0].roi;
61 
62   while (gegl_buffer_iterator_next (iter))
63     {
64       const guint8 *data_u8 = iter->items[0].data;
65       gint          ex      = roi->x + roi->width;
66       gint          ey      = roi->y + roi->height;
67 
68       /*  only check the pixels if this tile is not fully within the
69        *  currently computed bounds
70        */
71       if (roi->x < tx1 || ex > tx2 ||
72           roi->y < ty1 || ey > ty2)
73         {
74           /* Check upper left and lower right corners to see if we can
75            * avoid checking the rest of the pixels in this tile
76            */
77           if (! gegl_memeq_zero (data_u8,                            bpp) &&
78               ! gegl_memeq_zero (data_u8 + (iter->length - 1) * bpp, bpp))
79             {
80               /*  "ex/ey - 1" because the internal variables are the
81                *  right/bottom pixel of the mask's contents, not one
82                *  right/below it like the return values.
83                */
84 
85               if (roi->x < tx1) tx1 = roi->x;
86               if (ex > tx2)     tx2 = ex - 1;
87 
88               if (roi->y < ty1) ty1 = roi->y;
89               if (ey > ty2)     ty2 = ey - 1;
90             }
91           else
92             {
93               #define FIND_BOUNDS(bpp, type)                                 \
94                 G_STMT_START                                                 \
95                   {                                                          \
96                     const type *data;                                        \
97                     gint        y;                                           \
98                                                                              \
99                     if ((guintptr) data_u8 % bpp)                            \
100                       goto generic;                                          \
101                                                                              \
102                     data = (const type *) data_u8;                           \
103                                                                              \
104                     for (y = roi->y; y < ey; y++)                            \
105                       {                                                      \
106                         gint x1;                                             \
107                                                                              \
108                         for (x1 = 0; x1 < roi->width; x1++)                  \
109                           {                                                  \
110                             if (data[x1])                                    \
111                               {                                              \
112                                 gint x2;                                     \
113                                 gint x2_end = MAX (x1, tx2 - roi->x);        \
114                                                                              \
115                                 for (x2 = roi->width - 1; x2 > x2_end; x2--) \
116                                   {                                          \
117                                     if (data[x2])                            \
118                                       break;                                 \
119                                   }                                          \
120                                                                              \
121                                 x1 += roi->x;                                \
122                                 x2 += roi->x;                                \
123                                                                              \
124                                 if (x1 < tx1) tx1 = x1;                      \
125                                 if (x2 > tx2) tx2 = x2;                      \
126                                                                              \
127                                 if (y < ty1) ty1 = y;                        \
128                                 if (y > ty2) ty2 = y;                        \
129                                                                              \
130                                 break;                                       \
131                               }                                              \
132                           }                                                  \
133                                                                              \
134                         data += roi->width;                                  \
135                       }                                                      \
136                   }                                                          \
137                 G_STMT_END
138 
139               switch (bpp)
140                 {
141                 case 1:
142                   FIND_BOUNDS (1, guint8);
143                   break;
144 
145                 case 2:
146                   FIND_BOUNDS (2, guint16);
147                   break;
148 
149                 case 4:
150                   FIND_BOUNDS (4, guint32);
151                   break;
152 
153                 case 8:
154                   FIND_BOUNDS (8, guint64);
155                   break;
156 
157                 default:
158                 generic:
159                   {
160                     const guint8 *data = data_u8;
161                     gint          y;
162 
163                     for (y = roi->y; y < ey; y++)
164                       {
165                         gint x1;
166 
167                         for (x1 = 0; x1 < roi->width; x1++)
168                           {
169                             if (! gegl_memeq_zero (data + x1 * bpp, bpp))
170                               {
171                                 gint x2;
172                                 gint x2_end = MAX (x1, tx2 - roi->x);
173 
174                                 for (x2 = roi->width - 1; x2 > x2_end; x2--)
175                                   {
176                                     if (! gegl_memeq_zero (data + x2 * bpp,
177                                                            bpp))
178                                       {
179                                         break;
180                                       }
181                                   }
182 
183                                 x1 += roi->x;
184                                 x2 += roi->x;
185 
186                                 if (x1 < tx1) tx1 = x1;
187                                 if (x2 > tx2) tx2 = x2;
188 
189                                 if (y < ty1) ty1 = y;
190                                 if (y > ty2) ty2 = y;
191                               }
192                           }
193 
194                         data += roi->width * bpp;
195                       }
196                   }
197                   break;
198                 }
199 
200               #undef FIND_BOUNDS
201             }
202         }
203     }
204 
205   tx2 = CLAMP (tx2 + 1, 0, gegl_buffer_get_width  (buffer));
206   ty2 = CLAMP (ty2 + 1, 0, gegl_buffer_get_height (buffer));
207 
208   if (tx1 == gegl_buffer_get_width  (buffer) &&
209       ty1 == gegl_buffer_get_height (buffer))
210     {
211       *x1 = 0;
212       *y1 = 0;
213       *x2 = gegl_buffer_get_width  (buffer);
214       *y2 = gegl_buffer_get_height (buffer);
215 
216       return FALSE;
217     }
218 
219   *x1 = tx1;
220   *y1 = ty1;
221   *x2 = tx2;
222   *y2 = ty2;
223 
224   return TRUE;
225 }
226 
227 gboolean
gimp_gegl_mask_is_empty(GeglBuffer * buffer)228 gimp_gegl_mask_is_empty (GeglBuffer *buffer)
229 {
230   GeglBufferIterator *iter;
231   const Babl         *format;
232   gint                bpp;
233 
234   g_return_val_if_fail (GEGL_IS_BUFFER (buffer), FALSE);
235 
236   format = gegl_buffer_get_format (buffer);
237   bpp    = babl_format_get_bytes_per_pixel (format);
238 
239   iter = gegl_buffer_iterator_new (buffer, NULL, 0, format,
240                                    GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
241 
242   while (gegl_buffer_iterator_next (iter))
243     {
244       if (! gegl_memeq_zero (iter->items[0].data, bpp * iter->length))
245         {
246           gegl_buffer_iterator_stop (iter);
247 
248           return FALSE;
249         }
250     }
251 
252   return TRUE;
253 }
254