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