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