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