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