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