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