1 // Aseprite Document Library
2 // Copyright (c) 2001-2015 David Capello
3 //
4 // This file is released under the terms of the MIT license.
5 // Read LICENSE.txt for more information.
6
7 #ifdef HAVE_CONFIG_H
8 #include "config.h"
9 #endif
10
11 #include "doc/mask_boundaries.h"
12
13 #include "doc/image_impl.h"
14
15 namespace doc {
16
MaskBoundaries(const Image * bitmap)17 MaskBoundaries::MaskBoundaries(const Image* bitmap)
18 {
19 int x, y, w = bitmap->width(), h = bitmap->height();
20
21 const LockImageBits<BitmapTraits> bits(bitmap);
22 auto it = bits.begin(); // Current pixel iterator
23 #if _DEBUG
24 auto prevIt = bits.begin(); // Previous row iterator (same X pos)
25 #endif
26
27 // Vertical segments being expanded from the previous row.
28 std::vector<int> vertSegs(w+1, -1);
29
30 // Horizontal segment being expanded from the previous column.
31 int horzSeg;
32
33 #define new_hseg(open) { \
34 m_segs.push_back(Segment(open, gfx::Rect(x, y, 1, 0))); \
35 horzSeg = int(m_segs.size()-1); \
36 }
37 #define new_vseg(open) { \
38 m_segs.push_back(Segment(open, gfx::Rect(x, y, 0, 1))); \
39 vertSegs[x] = int(m_segs.size()-1); \
40 }
41 #define expand_hseg() { \
42 ASSERT(hseg); \
43 ++hseg->m_bounds.w; \
44 }
45 #define expand_vseg() { \
46 ASSERT(vseg); \
47 ++vseg->m_bounds.h; \
48 }
49 #define stop_expanding_hseg() { \
50 horzSeg = -1; \
51 }
52 #define stop_expanding_vseg() { \
53 vertSegs[x] = -1; \
54 }
55
56 for (y=0; y<=h; ++y) {
57 bool prevColor = false; // Previous color (X-1) same Y row
58 horzSeg = -1;
59
60 for (x=0; x<=w; ++x) {
61 bool color = (x < w && y < h && *it ? true: false);
62 #if _DEBUG
63 bool prevRowColor = (x < w && y > 0 && *prevIt ? true: false);
64 #endif
65 Segment* hseg = (horzSeg >= 0 ? &m_segs[horzSeg]: nullptr);
66 Segment* vseg = (vertSegs[x] >= 0 ? &m_segs[vertSegs[x]]: nullptr);
67
68 //
69 // - -
70 //
71 // - 1
72 //
73 if (color) {
74 //
75 // - | -
76 // o
77 // - 1
78 //
79 if (vseg) {
80 //
81 // 0 | 1
82 // o
83 // - 1
84 //
85 if (vseg->open()) {
86 ASSERT(prevRowColor);
87
88 //
89 // 0 | 1
90 // --x
91 // 1 1
92 //
93 if (hseg) {
94 ASSERT(hseg->open());
95 ASSERT(prevColor);
96 stop_expanding_hseg();
97 stop_expanding_vseg();
98 }
99 //
100 // 0 | 1
101 // |
102 // 0 | 1
103 // o
104 else {
105 ASSERT(!prevColor);
106 expand_vseg();
107 }
108 }
109 //
110 // 1 | 0
111 // x--o
112 // - 1
113 //
114 else {
115 ASSERT(!prevRowColor);
116
117 //
118 // 1 | 0
119 // --x--o
120 // 0 | 1
121 // o
122 if (hseg) {
123 ASSERT(!prevColor);
124 ASSERT(!hseg->open());
125 new_hseg(true);
126 new_vseg(true);
127 }
128 //
129 // 1 | 0
130 // x--o
131 // 1 1
132 //
133 else {
134 ASSERT(prevColor);
135 new_hseg(true);
136 stop_expanding_vseg();
137 }
138 }
139 }
140 //
141 // - - (there is no vertical segment in this row, both colors are equal)
142 //
143 // - 1
144 //
145 else {
146 //
147 // - -
148 // --o
149 // - 1
150 //
151 if (hseg) {
152 //
153 // 0 0
154 // -----o
155 // 1 1
156 //
157 if (hseg->open()) {
158 ASSERT(prevColor);
159 expand_hseg();
160 }
161 //
162 // 1 1
163 // --x
164 // 0 | 1
165 // o
166 else {
167 ASSERT(!prevColor);
168 stop_expanding_hseg();
169 new_vseg(true);
170 }
171 }
172 else {
173 //
174 // 1 1
175 //
176 // 1 1
177 //
178 if (prevColor) {
179 // Do nothing, we are inside boundaries
180 }
181 //
182 // 0 0
183 // --o
184 // 0 | 1
185 // o
186 else {
187 // First two segments of a corner
188 new_hseg(true);
189 new_vseg(true);
190 }
191 }
192 }
193 }
194 //
195 // - -
196 //
197 // - 0
198 //
199 else {
200 //
201 // - | -
202 // o
203 // - 0
204 //
205 if (vseg) {
206 //
207 // 0 | 1
208 // o
209 // - 0
210 //
211 if (vseg->open()) {
212 ASSERT(prevRowColor);
213
214 //
215 // 0 | 1
216 // --x--o
217 // 1 | 0
218 // o
219 if (hseg) {
220 ASSERT(hseg->open());
221 ASSERT(prevColor);
222 new_hseg(false);
223 new_vseg(false);
224 }
225 //
226 // 0 | 1
227 // x--o
228 // 0 0
229 //
230 else {
231 ASSERT(!prevColor);
232 new_hseg(false);
233 stop_expanding_vseg();
234 }
235 }
236 //
237 // 1 | 0
238 // o
239 // - 0
240 //
241 else {
242 ASSERT(!prevRowColor);
243
244 //
245 // 1 | 0
246 // --x
247 // 0 0
248 //
249 if (hseg) {
250 ASSERT(!prevColor);
251 stop_expanding_hseg();
252 stop_expanding_vseg();
253 }
254 //
255 // 1 | 0
256 // |
257 // 1 | 0
258 // o
259 else {
260 ASSERT(prevColor);
261 expand_vseg();
262 }
263 }
264 }
265 //
266 // - - (there is no vertical segment in this row, both colors are equal)
267 //
268 // - 0
269 //
270 else {
271 //
272 // - -
273 // --o
274 // - 0
275 //
276 if (hseg) {
277 //
278 // 0 0
279 // --x
280 // 1 | 0
281 // o
282 if (hseg->open()) {
283 ASSERT(prevColor);
284 stop_expanding_hseg();
285 new_vseg(false);
286 }
287 //
288 // 1 1
289 // -----o
290 // 0 0
291 //
292 else {
293 ASSERT(!prevColor);
294 expand_hseg();
295 }
296 }
297 else {
298 //
299 // 1 1
300 // --o
301 // 1 | 0
302 // o
303 if (prevColor) {
304 new_hseg(false);
305 new_vseg(false);
306 }
307 //
308 // 0 0
309 //
310 // 0 0
311 //
312 else {
313 // Do nothing, we are inside boundaries
314 }
315 }
316 }
317 }
318
319 prevColor = color;
320 if (x < w) {
321 #if _DEBUG
322 if (y > 0) ++prevIt;
323 #endif
324 if (y < h) ++it;
325 }
326 }
327 }
328
329 ASSERT(it == bits.end());
330 ASSERT(prevIt == bits.end());
331 }
332
offset(int x,int y)333 void MaskBoundaries::offset(int x, int y)
334 {
335 for (Segment& seg : m_segs)
336 seg.offset(x, y);
337 }
338
339 } // namespace doc
340