1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
#include <mbgl/sprite/sprite_parser.hpp>
#include <mbgl/style/image.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/rapidjson.hpp>
#include <mbgl/util/string.hpp>
#include <cmath>
#include <limits>
namespace mbgl {
std::unique_ptr<style::Image> createStyleImage(const std::string& id,
const PremultipliedImage& image,
const uint32_t srcX,
const uint32_t srcY,
const uint32_t width,
const uint32_t height,
const double ratio,
const bool sdf) {
// Disallow invalid parameter configurations.
if (width <= 0 || height <= 0 || width > 1024 || height > 1024 ||
ratio <= 0 || ratio > 10 ||
srcX >= image.size.width || srcY >= image.size.height ||
srcX + width > image.size.width || srcY + height > image.size.height) {
Log::Error(Event::Sprite, "Can't create sprite with invalid metrics: %ux%u@%u,%u in %ux%u@%sx sprite",
width, height, srcX, srcY,
image.size.width, image.size.height,
util::toString(ratio).c_str());
return nullptr;
}
PremultipliedImage dstImage({ width, height });
// Copy from the source image into our individual sprite image
PremultipliedImage::copy(image, dstImage, { srcX, srcY }, { 0, 0 }, { width, height });
return std::make_unique<style::Image>(id, std::move(dstImage), ratio, sdf);
}
namespace {
uint16_t getUInt16(const JSValue& value, const char* name, const uint16_t def = 0) {
if (value.HasMember(name)) {
auto& v = value[name];
if (v.IsUint() && v.GetUint() <= std::numeric_limits<uint16_t>::max()) {
return v.GetUint();
} else {
Log::Warning(Event::Sprite, "Value of '%s' must be an integer between 0 and 65535",
name);
}
}
return def;
}
double getDouble(const JSValue& value, const char* name, const double def = 0) {
if (value.HasMember(name)) {
auto& v = value[name];
if (v.IsNumber()) {
return v.GetDouble();
} else {
Log::Warning(Event::Sprite, "Value of '%s' must be a number", name);
}
}
return def;
}
bool getBoolean(const JSValue& value, const char* name, const bool def = false) {
if (value.HasMember(name)) {
auto& v = value[name];
if (v.IsBool()) {
return v.GetBool();
} else {
Log::Warning(Event::Sprite, "Value of '%s' must be a boolean", name);
}
}
return def;
}
} // namespace
std::vector<std::unique_ptr<style::Image>> parseSprite(const std::string& encodedImage, const std::string& json) {
const PremultipliedImage raster = decodeImage(encodedImage);
JSDocument doc;
doc.Parse<0>(json.c_str());
if (doc.HasParseError()) {
throw std::runtime_error("Failed to parse JSON: " + formatJSONParseError(doc));
} else if (!doc.IsObject()) {
throw std::runtime_error("Sprite JSON root must be an object");
} else {
std::vector<std::unique_ptr<style::Image>> images;
for (const auto& property : doc.GetObject()) {
const std::string name = { property.name.GetString(), property.name.GetStringLength() };
const JSValue& value = property.value;
if (value.IsObject()) {
const uint16_t x = getUInt16(value, "x", 0);
const uint16_t y = getUInt16(value, "y", 0);
const uint16_t width = getUInt16(value, "width", 0);
const uint16_t height = getUInt16(value, "height", 0);
const double pixelRatio = getDouble(value, "pixelRatio", 1);
const bool sdf = getBoolean(value, "sdf", false);
auto image = createStyleImage(name, raster, x, y, width, height, pixelRatio, sdf);
if (image) {
images.push_back(std::move(image));
}
}
}
return images;
}
}
} // namespace mbgl
|