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