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 | |
17 | void MaskBoundaries::reset() |
18 | { |
19 | m_segs.clear(); |
20 | if (!m_path.isEmpty()) |
21 | m_path.rewind(); |
22 | } |
23 | |
24 | void 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 | |
342 | void 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 | |
350 | void 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 | |