1 #pragma once 2 3 #include <mbgl/util/noncopyable.hpp> 4 #include <mbgl/util/geometry.hpp> 5 #include <mbgl/util/size.hpp> 6 7 #include <string> 8 #include <cstring> 9 #include <memory> 10 #include <algorithm> 11 12 namespace mbgl { 13 14 enum class ImageAlphaMode { 15 Unassociated, 16 Premultiplied, 17 Exclusive, // Alpha-channel only 18 }; 19 20 template <ImageAlphaMode Mode> 21 class Image : private util::noncopyable { 22 public: 23 Image() = default; 24 Image(Size size_)25 Image(Size size_) 26 : size(std::move(size_)), 27 data(std::make_unique<uint8_t[]>(bytes())) {} 28 Image(Size size_,const uint8_t * srcData,std::size_t srcLength)29 Image(Size size_, const uint8_t* srcData, std::size_t srcLength) 30 : size(std::move(size_)) { 31 if (srcLength != bytes()) { 32 throw std::invalid_argument("mismatched image size"); 33 } 34 data = std::make_unique<uint8_t[]>(bytes()); 35 std::copy(srcData, srcData + srcLength, data.get()); 36 } 37 Image(Size size_,std::unique_ptr<uint8_t[]> data_)38 Image(Size size_, std::unique_ptr<uint8_t[]> data_) 39 : size(std::move(size_)), 40 data(std::move(data_)) {} 41 Image(Image && o)42 Image(Image&& o) 43 : size(o.size), 44 data(std::move(o.data)) { 45 o.size.width = o.size.height = 0; 46 } 47 operator =(Image && o)48 Image& operator=(Image&& o) { 49 size = o.size; 50 data = std::move(o.data); 51 o.size.width = o.size.height = 0; 52 return *this; 53 } 54 operator ==(const Image & lhs,const Image & rhs)55 friend bool operator==(const Image& lhs, const Image& rhs) { 56 return std::equal(lhs.data.get(), lhs.data.get() + lhs.bytes(), 57 rhs.data.get(), rhs.data.get() + rhs.bytes()); 58 } 59 operator !=(const Image & lhs,const Image & rhs)60 friend bool operator!=(const Image& lhs, const Image& rhs) { 61 return !(lhs == rhs); 62 } 63 valid() const64 bool valid() const { 65 return !size.isEmpty() && data.get() != nullptr; 66 } 67 68 template <typename T = Image> clone() const69 T clone() const { 70 T copy_(size); 71 std::copy(data.get(), data.get() + bytes(), copy_.data.get()); 72 return copy_; 73 } 74 stride() const75 size_t stride() const { return channels * size.width; } bytes() const76 size_t bytes() const { return stride() * size.height; } 77 fill(uint8_t value)78 void fill(uint8_t value) { 79 std::fill(data.get(), data.get() + bytes(), value); 80 } 81 resize(Size size_)82 void resize(Size size_) { 83 if (size == size_) { 84 return; 85 } 86 Image newImage(size_); 87 newImage.fill(0); 88 copy(*this, newImage, {0, 0}, {0, 0}, { 89 std::min(size.width, size_.width), 90 std::min(size.height, size_.height) 91 }); 92 operator=(std::move(newImage)); 93 } 94 95 // Clears the rect area specified by `pt` and `size` from `dstImage`. clear(Image & dstImg,const Point<uint32_t> & pt,const Size & size)96 static void clear(Image& dstImg, const Point<uint32_t>& pt, const Size& size) { 97 if (size.isEmpty()) { 98 return; 99 } 100 101 if (!dstImg.valid()) { 102 throw std::invalid_argument("invalid destination for image clear"); 103 } 104 105 if (size.width > dstImg.size.width || 106 size.height > dstImg.size.height || 107 pt.x > dstImg.size.width - size.width || 108 pt.y > dstImg.size.height - size.height) { 109 throw std::out_of_range("out of range destination coordinates for image clear"); 110 } 111 112 uint8_t* dstData = dstImg.data.get(); 113 114 for (uint32_t y = 0; y < size.height; y++) { 115 const std::size_t dstOffset = (pt.y + y) * dstImg.stride() + pt.x * channels; 116 std::memset(dstData + dstOffset, 0, size.width * channels); 117 } 118 } 119 120 // Copy image data within `rect` from `src` to the rectangle of the same size at `pt` 121 // in `dst`. If the specified bounds exceed the bounds of the source or destination, 122 // throw `std::out_of_range`. Must not be used to move data within a single Image. copy(const Image & srcImg,Image & dstImg,const Point<uint32_t> & srcPt,const Point<uint32_t> & dstPt,const Size & size)123 static void copy(const Image& srcImg, Image& dstImg, const Point<uint32_t>& srcPt, const Point<uint32_t>& dstPt, const Size& size) { 124 if (size.isEmpty()) { 125 return; 126 } 127 128 if (!srcImg.valid()) { 129 throw std::invalid_argument("invalid source for image copy"); 130 } 131 132 if (!dstImg.valid()) { 133 throw std::invalid_argument("invalid destination for image copy"); 134 } 135 136 if (size.width > srcImg.size.width || 137 size.height > srcImg.size.height || 138 srcPt.x > srcImg.size.width - size.width || 139 srcPt.y > srcImg.size.height - size.height) { 140 throw std::out_of_range("out of range source coordinates for image copy"); 141 } 142 143 if (size.width > dstImg.size.width || 144 size.height > dstImg.size.height || 145 dstPt.x > dstImg.size.width - size.width || 146 dstPt.y > dstImg.size.height - size.height) { 147 throw std::out_of_range("out of range destination coordinates for image copy"); 148 } 149 150 const uint8_t* srcData = srcImg.data.get(); 151 uint8_t* dstData = dstImg.data.get(); 152 153 assert(srcData != dstData); 154 155 for (uint32_t y = 0; y < size.height; y++) { 156 const std::size_t srcOffset = (srcPt.y + y) * srcImg.stride() + srcPt.x * channels; 157 const std::size_t dstOffset = (dstPt.y + y) * dstImg.stride() + dstPt.x * channels; 158 std::copy(srcData + srcOffset, 159 srcData + srcOffset + size.width * channels, 160 dstData + dstOffset); 161 } 162 } 163 164 Size size; 165 static constexpr size_t channels = Mode == ImageAlphaMode::Exclusive ? 1 : 4; 166 std::unique_ptr<uint8_t[]> data; 167 }; 168 169 using UnassociatedImage = Image<ImageAlphaMode::Unassociated>; 170 using PremultipliedImage = Image<ImageAlphaMode::Premultiplied>; 171 using AlphaImage = Image<ImageAlphaMode::Exclusive>; 172 173 // TODO: don't use std::string for binary data. 174 PremultipliedImage decodeImage(const std::string&); 175 std::string encodePNG(const PremultipliedImage&); 176 177 } // namespace mbgl 178