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
12namespace mbgl {
13
14enum class ImageAlphaMode {
15 Unassociated,
16 Premultiplied,
17 Exclusive, // Alpha-channel only
18};
19
20template <ImageAlphaMode Mode>
21class Image : private util::noncopyable {
22public:
23 Image() = default;
24
25 Image(Size size_)
26 : size(std::move(size_)),
27 data(std::make_unique<uint8_t[]>(bytes())) {}
28
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
38 Image(Size size_, std::unique_ptr<uint8_t[]> data_)
39 : size(std::move(size_)),
40 data(std::move(data_)) {}
41
42 Image(Image&& o)
43 : size(o.size),
44 data(std::move(o.data)) {
45 o.size.width = o.size.height = 0;
46 }
47
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
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
60 friend bool operator!=(const Image& lhs, const Image& rhs) {
61 return !(lhs == rhs);
62 }
63
64 bool valid() const {
65 return !size.isEmpty() && data.get() != nullptr;
66 }
67
68 template <typename T = Image>
69 T clone() const {
70 T copy_(size);
71 std::copy(data.get(), data.get() + bytes(), copy_.data.get());
72 return copy_;
73 }
74
75 size_t stride() const { return channels * size.width; }
76 size_t bytes() const { return stride() * size.height; }
77
78 void fill(uint8_t value) {
79 std::fill(data.get(), data.get() + bytes(), value);
80 }
81
82 void resize(Size size_) {
83 if (size == size_) {
84 return;
85 }
86 Image newImage(size_);
87 newImage.fill(0);
88 copy(srcImg: *this, dstImg&: newImage, srcPt: {0, 0}, dstPt: {0, 0}, size: {
89 std::min(a: size.width, b: size_.width),
90 std::min(a: size.height, b: size_.height)
91 });
92 operator=(o: std::move(newImage));
93 }
94
95 // Clears the rect area specified by `pt` and `size` from `dstImage`.
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(s: dstData + dstOffset, c: 0, n: 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.
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
169using UnassociatedImage = Image<ImageAlphaMode::Unassociated>;
170using PremultipliedImage = Image<ImageAlphaMode::Premultiplied>;
171using AlphaImage = Image<ImageAlphaMode::Exclusive>;
172
173// TODO: don't use std::string for binary data.
174PremultipliedImage decodeImage(const std::string&);
175std::string encodePNG(const PremultipliedImage&);
176
177} // namespace mbgl
178

source code of qtlocation/src/3rdparty/mapbox-gl-native/include/mbgl/util/image.hpp