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