1// Aseprite Document Library
2// Copyright (C) 2018-2021 Igara Studio S.A.
3// Copyright (C) 2001-2016 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_IMPL_H_INCLUDED
9#define DOC_IMAGE_IMPL_H_INCLUDED
10#pragma once
11
12#include <algorithm>
13#include <cstdlib>
14#include <cstring>
15
16#include "doc/blend_funcs.h"
17#include "doc/image.h"
18#include "doc/image_bits.h"
19#include "doc/image_iterator.h"
20#include "doc/palette.h"
21
22namespace doc {
23
24 template<typename ImageTraits> class LockImageBits;
25
26 template<class Traits>
27 class ImageImpl : public Image {
28 private:
29 typedef typename Traits::address_t address_t;
30 typedef typename Traits::const_address_t const_address_t;
31
32 ImageBufferPtr m_buffer;
33 address_t m_bits;
34 address_t* m_rows;
35
36 inline address_t getBitsAddress() {
37 return m_bits;
38 }
39
40 inline const_address_t getBitsAddress() const {
41 return m_bits;
42 }
43
44 inline address_t getLineAddress(int y) {
45 ASSERT(y >= 0 && y < height());
46 return m_rows[y];
47 }
48
49 inline const_address_t getLineAddress(int y) const {
50 ASSERT(y >= 0 && y < height());
51 return m_rows[y];
52 }
53
54 public:
55 inline address_t address(int x, int y) const {
56 return (address_t)(getLineAddress(y) + x / (Traits::pixels_per_byte == 0 ? 1 : Traits::pixels_per_byte));
57 }
58
59 ImageImpl(const ImageSpec& spec,
60 const ImageBufferPtr& buffer)
61 : Image(spec)
62 , m_buffer(buffer)
63 {
64 ASSERT(Traits::color_mode == spec.colorMode());
65
66 std::size_t for_rows = sizeof(address_t) * spec.height();
67 std::size_t rowstride_bytes = Traits::getRowStrideBytes(spec.width());
68 std::size_t required_size = for_rows + rowstride_bytes*spec.height();
69
70 if (!m_buffer)
71 m_buffer = std::make_shared<ImageBuffer>(required_size);
72 else
73 m_buffer->resizeIfNecessary(required_size);
74
75 std::fill(m_buffer->buffer(),
76 m_buffer->buffer()+required_size, 0);
77
78 m_rows = (address_t*)m_buffer->buffer();
79 m_bits = (address_t)(m_buffer->buffer() + for_rows);
80
81 address_t addr = m_bits;
82 for (int y=0; y<spec.height(); ++y) {
83 m_rows[y] = addr;
84 addr = (address_t)(((uint8_t*)addr) + rowstride_bytes);
85 }
86 }
87
88 uint8_t* getPixelAddress(int x, int y) const override {
89 ASSERT(x >= 0 && x < width());
90 ASSERT(y >= 0 && y < height());
91
92 return (uint8_t*)address(x, y);
93 }
94
95 color_t getPixel(int x, int y) const override {
96 ASSERT(x >= 0 && x < width());
97 ASSERT(y >= 0 && y < height());
98
99 return *address(x, y);
100 }
101
102 void putPixel(int x, int y, color_t color) override {
103 ASSERT(x >= 0 && x < width());
104 ASSERT(y >= 0 && y < height());
105
106 *address(x, y) = color;
107 }
108
109 void clear(color_t color) override {
110 int w = width();
111 int h = height();
112
113 // Fill the first line
114 address_t first = address(0, 0);
115 std::fill(first, first+w, color);
116
117 // Copy the first line into all other lines
118 for (int y=1; y<h; ++y)
119 std::copy(first, first+w, address(0, y));
120 }
121
122 void copy(const Image* _src, gfx::Clip area) override {
123 const ImageImpl<Traits>* src = (const ImageImpl<Traits>*)_src;
124 address_t src_address;
125 address_t dst_address;
126
127 if (!area.clip(width(), height(), src->width(), src->height()))
128 return;
129
130 for (int end_y=area.dst.y+area.size.h;
131 area.dst.y<end_y;
132 ++area.dst.y, ++area.src.y) {
133 src_address = src->address(area.src.x, area.src.y);
134 dst_address = address(area.dst.x, area.dst.y);
135
136 std::copy(src_address,
137 src_address + area.size.w,
138 dst_address);
139 }
140 }
141
142 void drawHLine(int x1, int y, int x2, color_t color) override {
143 LockImageBits<Traits> bits(this, gfx::Rect(x1, y, x2 - x1 + 1, 1));
144 typename LockImageBits<Traits>::iterator it(bits.begin());
145 typename LockImageBits<Traits>::iterator end(bits.end());
146
147 for (; it != end; ++it)
148 *it = color;
149 }
150
151 void fillRect(int x1, int y1, int x2, int y2, color_t color) override {
152 // Fill the first line
153 ImageImpl<Traits>::drawHLine(x1, y1, x2, color);
154
155 // Copy all other lines
156 address_t first = address(x1, y1);
157 int w = x2 - x1 + 1;
158 for (int y=y1; y<=y2; ++y)
159 std::copy(first, first+w, address(x1, y));
160 }
161
162 void blendRect(int x1, int y1, int x2, int y2, color_t color, int opacity) override {
163 fillRect(x1, y1, x2, y2, color);
164 }
165
166 private:
167 bool clip_rects(const Image* src, int& dst_x, int& dst_y, int& src_x, int& src_y, int& w, int& h) const {
168 // Clip with destionation image
169 if (dst_x < 0) {
170 w += dst_x;
171 src_x -= dst_x;
172 dst_x = 0;
173 }
174 if (dst_y < 0) {
175 h += dst_y;
176 src_y -= dst_y;
177 dst_y = 0;
178 }
179 if (dst_x+w > width()) {
180 w = width() - dst_x;
181 }
182 if (dst_y+h > height()) {
183 h = height() - dst_y;
184 }
185
186 // Clip with source image
187 if (src_x < 0) {
188 w += src_x;
189 dst_x -= src_x;
190 src_x = 0;
191 }
192 if (src_y < 0) {
193 h += src_y;
194 dst_y -= src_y;
195 src_y = 0;
196 }
197 if (src_x+w > src->width()) {
198 w = src->width() - src_x;
199 }
200 if (src_y+h > src->height()) {
201 h = src->height() - src_y;
202 }
203
204 // Empty cases
205 if (w < 1 || h < 1)
206 return false;
207
208 if ((src_x+w <= 0) || (src_x >= src->width()) ||
209 (src_y+h <= 0) || (src_y >= src->height()))
210 return false;
211
212 if ((dst_x+w <= 0) || (dst_x >= width()) ||
213 (dst_y+h <= 0) || (dst_y >= height()))
214 return false;
215
216 // Check this function is working correctly
217 ASSERT(src->bounds().contains(gfx::Rect(src_x, src_y, w, h)));
218 ASSERT(bounds().contains(gfx::Rect(dst_x, dst_y, w, h)));
219 return true;
220 }
221 };
222
223 //////////////////////////////////////////////////////////////////////
224 // Specializations
225
226 template<>
227 inline void ImageImpl<IndexedTraits>::clear(color_t color) {
228 std::fill(getBitsAddress(),
229 getBitsAddress() + width()*height(),
230 color);
231 }
232
233 template<>
234 inline void ImageImpl<BitmapTraits>::clear(color_t color) {
235 std::fill(getBitsAddress(),
236 getBitsAddress() + BitmapTraits::getRowStrideBytes(width()) * height(),
237 (color ? 0xff: 0x00));
238 }
239
240 template<>
241 inline color_t ImageImpl<BitmapTraits>::getPixel(int x, int y) const {
242 ASSERT(x >= 0 && x < width());
243 ASSERT(y >= 0 && y < height());
244
245 std::div_t d = std::div(x, 8);
246 return ((*(getLineAddress(y) + d.quot)) & (1<<d.rem)) ? 1: 0;
247 }
248
249 template<>
250 inline void ImageImpl<BitmapTraits>::putPixel(int x, int y, color_t color) {
251 ASSERT(x >= 0 && x < width());
252 ASSERT(y >= 0 && y < height());
253
254 std::div_t d = std::div(x, 8);
255 if (color)
256 (*(getLineAddress(y) + d.quot)) |= (1 << d.rem);
257 else
258 (*(getLineAddress(y) + d.quot)) &= ~(1 << d.rem);
259 }
260
261 template<>
262 inline void ImageImpl<BitmapTraits>::fillRect(int x1, int y1, int x2, int y2, color_t color) {
263 for (int y=y1; y<=y2; ++y)
264 ImageImpl<BitmapTraits>::drawHLine(x1, y, x2, color);
265 }
266
267 template<>
268 inline void ImageImpl<RgbTraits>::blendRect(int x1, int y1, int x2, int y2, color_t color, int opacity) {
269 address_t addr;
270 int x, y;
271
272 for (y=y1; y<=y2; ++y) {
273 addr = (address_t)getPixelAddress(x1, y);
274 for (x=x1; x<=x2; ++x) {
275 *addr = rgba_blender_normal(*addr, color, opacity);
276 ++addr;
277 }
278 }
279 }
280
281 void copy_bitmaps(Image* dst, const Image* src, gfx::Clip area);
282 template<>
283 inline void ImageImpl<BitmapTraits>::copy(const Image* src, gfx::Clip area) {
284 copy_bitmaps(this, src, area);
285 }
286
287} // namespace doc
288
289#endif
290