1// Aseprite Document Library
2// Copyright (c) 2019-2022 Igara Studio S.A.
3// Copyright (c) 2001-2015 David Capello
4//
5// This file is released under the terms of the MIT license.
6// Read LICENSE.txt for more information.
7
8#ifndef DOC_IMAGE_ITERATOR_H_INCLUDED
9#define DOC_IMAGE_ITERATOR_H_INCLUDED
10#pragma once
11
12#include "doc/color.h"
13#include "doc/primitives_fast.h"
14#include "gfx/point.h"
15#include "gfx/rect.h"
16
17#include <cstdlib>
18
19#include <iostream>
20
21namespace doc {
22
23 class Image;
24
25 template<typename ImageTraits,
26 typename PointerType,
27 typename ReferenceType>
28 class ImageIteratorT {
29 public:
30 using iterator_category = std::forward_iterator_tag;
31 using value_type = typename ImageTraits::pixel_t;
32 using difference_type = std::ptrdiff_t;
33 using pointer = PointerType;
34 using reference = ReferenceType;
35
36 ImageIteratorT() : m_ptr(nullptr) {
37 }
38
39 ImageIteratorT(const ImageIteratorT& other) :
40 m_image(other.m_image),
41 m_ptr(other.m_ptr),
42 m_x(other.m_x),
43 m_y(other.m_y),
44 m_xbegin(other.m_xbegin),
45 m_xend(other.m_xend)
46 {
47 }
48
49 ImageIteratorT(const Image* image, const gfx::Rect& bounds, int x, int y) :
50 m_image(const_cast<Image*>(image)),
51 m_ptr(get_pixel_address_fast<ImageTraits>(image, x, y)),
52 m_x(x),
53 m_y(y),
54 m_xbegin(bounds.x),
55 m_xend(bounds.x + bounds.w)
56 {
57 ASSERT(bounds.contains(gfx::Point(x, y)));
58 ASSERT(image->bounds().contains(bounds));
59 }
60
61 ImageIteratorT& operator=(const ImageIteratorT& other) {
62 m_image = other.m_image;
63 m_ptr = other.m_ptr;
64 m_x = other.m_x;
65 m_y = other.m_y;
66 m_xbegin = other.m_xbegin;
67 m_xend = other.m_xend;
68 return *this;
69 }
70
71 bool operator==(const ImageIteratorT& other) const {
72 if (m_ptr == other.m_ptr) {
73 ASSERT(m_x == other.m_x && m_y == other.m_y);
74 }
75 else {
76 ASSERT(m_x != other.m_x || m_y != other.m_y);
77 }
78 return m_ptr == other.m_ptr;
79 }
80 bool operator!=(const ImageIteratorT& other) const {
81 if (m_ptr != other.m_ptr) {
82 ASSERT(m_x != other.m_x || m_y != other.m_y);
83 }
84 else {
85 ASSERT(m_x == other.m_x && m_y == other.m_y);
86 }
87 return m_ptr != other.m_ptr;
88 }
89 bool operator<(const ImageIteratorT& other) const { return m_ptr < other.m_ptr; }
90 bool operator>(const ImageIteratorT& other) const { return m_ptr > other.m_ptr; }
91 bool operator<=(const ImageIteratorT& other) const { return m_ptr <= other.m_ptr; }
92 bool operator>=(const ImageIteratorT& other) const { return m_ptr >= other.m_ptr; }
93
94 ImageIteratorT& operator++() {
95 ASSERT(m_image->bounds().contains(gfx::Point(m_x, m_y)));
96
97 ++m_ptr;
98 ++m_x;
99
100 if (m_x == m_xend) {
101 m_x = m_xbegin;
102 ++m_y;
103
104 if (m_y < m_image->height())
105 m_ptr = get_pixel_address_fast<ImageTraits>(m_image, m_x, m_y);
106 }
107
108 return *this;
109 }
110
111 ImageIteratorT& operator+=(int diff) {
112 while (diff-- > 0)
113 operator++();
114 return *this;
115 }
116
117 ImageIteratorT operator++(int) {
118 ImageIteratorT tmp(*this);
119 operator++();
120 return tmp;
121 }
122
123 reference operator*() { return *m_ptr; }
124
125 int x() const { return m_x; }
126 int y() const { return m_y; }
127
128 private:
129 Image* m_image;
130 pointer m_ptr;
131 int m_x, m_y;
132 int m_xbegin;
133 int m_xend;
134 };
135
136 template<typename ImageTraits>
137 class ImageIterator : public ImageIteratorT<ImageTraits,
138 typename ImageTraits::pixel_t *,
139 typename ImageTraits::pixel_t&> {
140 public:
141 ImageIterator() {
142 }
143
144 ImageIterator(const Image* image, const gfx::Rect& bounds, int x, int y) :
145 ImageIteratorT<ImageTraits,
146 typename ImageIterator::pointer,
147 typename ImageIterator::reference>(image, bounds, x, y) {
148 }
149 };
150
151 template<typename ImageTraits>
152 class ImageConstIterator : public ImageIteratorT<ImageTraits,
153 typename ImageTraits::pixel_t const *,
154 typename ImageTraits::pixel_t const &> {
155 public:
156 ImageConstIterator() {
157 }
158
159 ImageConstIterator(const Image* image, const gfx::Rect& bounds, int x, int y) :
160 ImageIteratorT<ImageTraits,
161 typename ImageConstIterator::pointer,
162 typename ImageConstIterator::reference>(image, bounds, x, y) {
163 }
164 };
165
166 //////////////////////////////////////////////////////////////////////
167 // Iterator for BitmapTraits
168
169 class BitPixelAccess {
170 public:
171 BitPixelAccess() :
172 m_ptr(nullptr),
173 m_bit(0) {
174 }
175
176 void reset(BitmapTraits::address_t ptr, int bit) {
177 m_ptr = ptr;
178 m_bit = bit;
179 }
180
181 void reset(BitmapTraits::pixel_t const* ptr, int bit) {
182 m_ptr = const_cast<BitmapTraits::address_t>(ptr);
183 m_bit = bit;
184 }
185
186 operator color_t() const {
187 return (*m_ptr & m_bit) ? 1: 0;
188 }
189
190 BitPixelAccess& operator=(color_t value) {
191 if (value)
192 *m_ptr |= m_bit;
193 else
194 *m_ptr &= ~m_bit;
195 return *this;
196 }
197
198 // It doesn't copy the BitPixelAccess, it must copy the bit from
199 // "other" to "this".
200 BitPixelAccess& operator=(const BitPixelAccess& other) {
201 return this->operator=((color_t)other);
202 }
203
204 bool operator==(int b) const {
205 return (color_t(*this) == color_t(b));
206 }
207
208 bool operator==(color_t b) const {
209 return (color_t(*this) == b);
210 }
211
212 bool operator==(const BitPixelAccess& b) const {
213 return (color_t(*this) == color_t(b));
214 }
215
216 bool operator!=(int b) const {
217 return (color_t(*this) != color_t(b));
218 }
219
220 bool operator!=(color_t b) const {
221 return (color_t(*this) != b);
222 }
223
224 bool operator!=(const BitPixelAccess& b) const {
225 return (color_t(*this) != color_t(b));
226 }
227
228 private:
229 // Non-copyable by copy constructor.
230 BitPixelAccess(const BitPixelAccess& other);
231
232 BitmapTraits::address_t m_ptr;
233 int m_bit;
234 };
235
236 inline bool operator==(int a, const BitPixelAccess& b) {
237 return (color_t(a) == color_t(b));
238 }
239
240 inline bool operator==(color_t a, const BitPixelAccess& b) {
241 return (a == color_t(b));
242 }
243
244 inline bool operator!=(int a, const BitPixelAccess& b) {
245 return (color_t(a) != color_t(b));
246 }
247
248 inline bool operator!=(color_t a, const BitPixelAccess& b) {
249 return (a != color_t(b));
250 }
251
252 template<typename PointerType,
253 typename ReferenceType>
254 class ImageIteratorT<BitmapTraits, PointerType, ReferenceType> {
255 public:
256 using iterator_category = std::forward_iterator_tag;
257 using value_type = BitmapTraits::pixel_t;
258 using difference_type = std::ptrdiff_t;
259 using pointer = PointerType;
260 using reference = ReferenceType;
261
262 enum { pixels_per_byte = BitmapTraits::pixels_per_byte };
263
264 ImageIteratorT() : m_ptr(nullptr) {
265 }
266
267 ImageIteratorT(const ImageIteratorT& other) :
268 m_image(other.m_image),
269 m_ptr(other.m_ptr),
270 m_x(other.m_x),
271 m_y(other.m_y),
272 m_subPixel(other.m_subPixel),
273 m_xbegin(other.m_xbegin),
274 m_xend(other.m_xend)
275 {
276 }
277
278 ImageIteratorT(const Image* image, const gfx::Rect& bounds, int x, int y) :
279 m_image(const_cast<Image*>(image)),
280 m_ptr(get_pixel_address_fast<BitmapTraits>(image, x, y)),
281 m_x(x),
282 m_y(y),
283 m_subPixel(x % 8),
284 m_xbegin(bounds.x),
285 m_xend(bounds.x + bounds.w)
286 {
287 ASSERT(bounds.contains(gfx::Point(x, y)));
288 }
289
290 ImageIteratorT& operator=(const ImageIteratorT& other) {
291 m_image = other.m_image;
292 m_ptr = other.m_ptr;
293 m_x = other.m_x;
294 m_y = other.m_y;
295 m_subPixel = other.m_subPixel;
296 m_xbegin = other.m_xbegin;
297 m_xend = other.m_xend;
298 return *this;
299 }
300
301 bool operator==(const ImageIteratorT& other) const {
302 if (m_ptr == other.m_ptr &&
303 m_subPixel == other.m_subPixel) {
304 ASSERT(m_x == other.m_x && m_y == other.m_y);
305 }
306 else {
307 ASSERT(m_x != other.m_x || m_y != other.m_y);
308 }
309 return m_ptr == other.m_ptr;
310 }
311 bool operator!=(const ImageIteratorT& other) const {
312 if (m_ptr != other.m_ptr ||
313 m_subPixel != other.m_subPixel) {
314 ASSERT(m_x != other.m_x || m_y != other.m_y);
315 }
316 else {
317 ASSERT(m_x == other.m_x && m_y == other.m_y);
318 }
319 return m_ptr != other.m_ptr;
320 }
321
322 ImageIteratorT& operator++() {
323 ASSERT(m_image->bounds().contains(gfx::Point(m_x, m_y)));
324
325 ++m_x;
326 ++m_subPixel;
327
328 if (m_x == m_xend) {
329 m_x = m_xbegin;
330 m_subPixel = m_x % 8;
331 ++m_y;
332
333 if (m_y < m_image->height())
334 m_ptr = get_pixel_address_fast<BitmapTraits>(m_image, m_x, m_y);
335 else
336 ++m_ptr;
337 }
338 else if (m_subPixel == 8) {
339 m_subPixel = 0;
340 ++m_ptr;
341 }
342
343 return *this;
344 }
345
346 ImageIteratorT& operator+=(int diff) {
347 while (diff-- > 0)
348 operator++();
349 return *this;
350 }
351
352 ImageIteratorT operator++(int) {
353 ImageIteratorT tmp(*this);
354 operator++();
355 return tmp;
356 }
357
358 reference operator*() const {
359 m_access.reset(m_ptr, 1 << m_subPixel);
360 return m_access;
361 }
362
363 reference operator*() {
364 m_access.reset(m_ptr, 1 << m_subPixel);
365 return m_access;
366 }
367
368 int x() const { return m_x; }
369 int y() const { return m_y; }
370
371 private:
372 Image* m_image;
373 pointer m_ptr;
374 int m_x, m_y;
375 int m_subPixel;
376 int m_xbegin;
377 int m_xend;
378 mutable BitPixelAccess m_access;
379 };
380
381 template<>
382 class ImageIterator<BitmapTraits> : public ImageIteratorT<BitmapTraits,
383 uint8_t*,
384 BitPixelAccess&> {
385 public:
386 typedef ImageIteratorT<BitmapTraits,
387 uint8_t*,
388 BitPixelAccess&> Base;
389
390 ImageIterator() {
391 }
392
393 ImageIterator(const Image* image, const gfx::Rect& bounds, int x, int y) :
394 Base(image, bounds, x, y) {
395 }
396 };
397
398 template<>
399 class ImageConstIterator<BitmapTraits> : public ImageIteratorT<BitmapTraits,
400 uint8_t const*,
401 const BitPixelAccess&> {
402 public:
403 typedef ImageIteratorT<BitmapTraits,
404 uint8_t const*,
405 const BitPixelAccess&> Base;
406
407 ImageConstIterator() {
408 }
409
410 ImageConstIterator(const Image* image, const gfx::Rect& bounds, int x, int y) :
411 Base(image, bounds, x, y) {
412 }
413 };
414
415} // namespace doc
416
417#endif
418