From 7821dccae5d20b697eee2b1ec8b8749878392286 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Tue, 20 Oct 2015 16:40:34 -0700 Subject: [PATCH] [core] Polymorphic layout and paint parsing --- src/mbgl/layer/background_layer.cpp | 9 + src/mbgl/layer/background_layer.hpp | 5 + src/mbgl/layer/circle_layer.cpp | 12 + src/mbgl/layer/circle_layer.hpp | 5 + src/mbgl/layer/fill_layer.cpp | 17 + src/mbgl/layer/fill_layer.hpp | 5 + src/mbgl/layer/line_layer.cpp | 30 + src/mbgl/layer/line_layer.hpp | 5 + src/mbgl/layer/raster_layer.cpp | 19 + src/mbgl/layer/raster_layer.hpp | 5 + src/mbgl/layer/symbol_layer.cpp | 71 ++ src/mbgl/layer/symbol_layer.hpp | 5 + src/mbgl/style/paint_properties_map.cpp | 12 + src/mbgl/style/paint_properties_map.hpp | 8 + .../style/piecewisefunction_properties.hpp | 4 +- src/mbgl/style/property_parsing.cpp | 434 +++++++++ src/mbgl/style/property_parsing.hpp | 57 ++ src/mbgl/style/style_layer.hpp | 11 +- src/mbgl/style/style_parser.cpp | 835 ++---------------- src/mbgl/style/style_parser.hpp | 78 +- .../style_parser/circle-color.info.json | 2 +- .../style_parser/line-translate.info.json | 2 +- 22 files changed, 810 insertions(+), 821 deletions(-) create mode 100644 src/mbgl/style/property_parsing.cpp create mode 100644 src/mbgl/style/property_parsing.hpp diff --git a/src/mbgl/layer/background_layer.cpp b/src/mbgl/layer/background_layer.cpp index 31e524bc8..70c6488ce 100644 --- a/src/mbgl/layer/background_layer.cpp +++ b/src/mbgl/layer/background_layer.cpp @@ -1,7 +1,16 @@ #include +#include namespace mbgl { +void BackgroundLayer::parsePaints(const JSVal& layer) { + paints.parseEach(layer, [&] (ClassProperties& paint, const JSVal& value) { + parseProperty>("background-opacity", PropertyKey::BackgroundOpacity, paint, value); + parseProperty>("background-color", PropertyKey::BackgroundColor, paint, value); + parseProperty>>("background-pattern", PropertyKey::BackgroundImage, paint, value, "background-pattern-transition"); + }); +} + void BackgroundLayer::recalculate(const StyleCalculationParameters& parameters) { paints.removeExpiredTransitions(parameters.now); diff --git a/src/mbgl/layer/background_layer.hpp b/src/mbgl/layer/background_layer.hpp index 8fe689ee6..86dfd24a4 100644 --- a/src/mbgl/layer/background_layer.hpp +++ b/src/mbgl/layer/background_layer.hpp @@ -3,11 +3,16 @@ #include #include +#include +#include namespace mbgl { class BackgroundLayer : public StyleLayer { public: + void parseLayout(const JSVal&) override {}; + void parsePaints(const JSVal&) override; + void recalculate(const StyleCalculationParameters&) override; BackgroundPaintProperties properties; diff --git a/src/mbgl/layer/circle_layer.cpp b/src/mbgl/layer/circle_layer.cpp index 0f16b02d3..e3fa4b4de 100644 --- a/src/mbgl/layer/circle_layer.cpp +++ b/src/mbgl/layer/circle_layer.cpp @@ -1,7 +1,19 @@ #include +#include namespace mbgl { +void CircleLayer::parsePaints(const JSVal& layer) { + paints.parseEach(layer, [&] (ClassProperties& paint, const JSVal& value) { + parseProperty>("circle-radius", PropertyKey::CircleRadius, paint, value); + parseProperty>("circle-color", PropertyKey::CircleColor, paint, value); + parseProperty>("circle-opacity", PropertyKey::CircleOpacity, paint, value); + parseProperty>>("circle-translate", PropertyKey::CircleTranslate, paint, value); + parseProperty>("circle-translate-anchor", PropertyKey::CircleTranslateAnchor, paint, value); + parseProperty>("circle-blur", PropertyKey::CircleBlur, paint, value); + }); +} + void CircleLayer::recalculate(const StyleCalculationParameters& parameters) { paints.removeExpiredTransitions(parameters.now); diff --git a/src/mbgl/layer/circle_layer.hpp b/src/mbgl/layer/circle_layer.hpp index ae1f68198..957d6b9ca 100644 --- a/src/mbgl/layer/circle_layer.hpp +++ b/src/mbgl/layer/circle_layer.hpp @@ -3,11 +3,16 @@ #include #include +#include +#include namespace mbgl { class CircleLayer : public StyleLayer { public: + void parseLayout(const JSVal&) override {}; + void parsePaints(const JSVal&) override; + void recalculate(const StyleCalculationParameters&) override; CirclePaintProperties properties; diff --git a/src/mbgl/layer/fill_layer.cpp b/src/mbgl/layer/fill_layer.cpp index ba3001efa..53bf0ac89 100644 --- a/src/mbgl/layer/fill_layer.cpp +++ b/src/mbgl/layer/fill_layer.cpp @@ -1,7 +1,24 @@ #include +#include namespace mbgl { +void FillLayer::parsePaints(const JSVal& layer) { + paints.parseEach(layer, [&] (ClassProperties& paint, const JSVal& value) { + parseProperty>("fill-antialias", PropertyKey::FillAntialias, paint, value); + parseProperty>("fill-opacity", PropertyKey::FillOpacity, paint, value); + parseProperty("fill-opacity-transition", PropertyKey::FillOpacity, paint, value); + parseProperty>("fill-color", PropertyKey::FillColor, paint, value); + parseProperty("fill-color-transition", PropertyKey::FillColor, paint, value); + parseProperty>("fill-outline-color", PropertyKey::FillOutlineColor, paint, value); + parseProperty("fill-outline-color-transition", PropertyKey::FillOutlineColor, paint, value); + parseProperty>>("fill-translate", PropertyKey::FillTranslate, paint, value); + parseProperty("fill-translate-transition", PropertyKey::FillTranslate, paint, value); + parseProperty>("fill-translate-anchor", PropertyKey::FillTranslateAnchor, paint, value); + parseProperty>>("fill-pattern", PropertyKey::FillImage, paint, value, "fill-pattern-transition"); + }); +} + void FillLayer::recalculate(const StyleCalculationParameters& parameters) { paints.removeExpiredTransitions(parameters.now); diff --git a/src/mbgl/layer/fill_layer.hpp b/src/mbgl/layer/fill_layer.hpp index 59ccaf36c..7a5d468bb 100644 --- a/src/mbgl/layer/fill_layer.hpp +++ b/src/mbgl/layer/fill_layer.hpp @@ -3,11 +3,16 @@ #include #include +#include +#include namespace mbgl { class FillLayer : public StyleLayer { public: + void parseLayout(const JSVal&) override {}; + void parsePaints(const JSVal&) override; + void recalculate(const StyleCalculationParameters&) override; FillPaintProperties properties; diff --git a/src/mbgl/layer/line_layer.cpp b/src/mbgl/layer/line_layer.cpp index 455674102..fe660638a 100644 --- a/src/mbgl/layer/line_layer.cpp +++ b/src/mbgl/layer/line_layer.cpp @@ -1,7 +1,37 @@ #include +#include +#include +#include namespace mbgl { +void LineLayer::parseLayout(const JSVal& value) { + parseProperty>("line-cap", PropertyKey::LineCap, bucket->layout, value); + parseProperty>("line-join", PropertyKey::LineJoin, bucket->layout, value); + parseProperty>("line-miter-limit", PropertyKey::LineMiterLimit, bucket->layout, value); + parseProperty>("line-round-limit", PropertyKey::LineRoundLimit, bucket->layout, value); +} + +void LineLayer::parsePaints(const JSVal& layer) { + paints.parseEach(layer, [&] (ClassProperties& paint, const JSVal& value) { + parseProperty>("line-opacity", PropertyKey::LineOpacity, paint, value); + parseProperty("line-opacity-transition", PropertyKey::LineOpacity, paint, value); + parseProperty>("line-color", PropertyKey::LineColor, paint, value); + parseProperty("line-color-transition", PropertyKey::LineColor, paint, value); + parseProperty>>("line-translate", PropertyKey::LineTranslate, paint, value); + parseProperty("line-translate-transition", PropertyKey::LineTranslate, paint, value); + parseProperty>("line-translate-anchor", PropertyKey::LineTranslateAnchor, paint, value); + parseProperty>("line-width", PropertyKey::LineWidth, paint, value); + parseProperty("line-width-transition", PropertyKey::LineWidth, paint, value); + parseProperty>("line-gap-width", PropertyKey::LineGapWidth, paint, value); + parseProperty("line-gap-width-transition", PropertyKey::LineGapWidth, paint, value); + parseProperty>("line-blur", PropertyKey::LineBlur, paint, value); + parseProperty("line-blur-transition", PropertyKey::LineBlur, paint, value); + parseProperty>>>("line-dasharray", PropertyKey::LineDashArray, paint, value, "line-dasharray-transition"); + parseProperty>>("line-pattern", PropertyKey::LineImage, paint, value, "line-pattern-transition"); + }); +} + void LineLayer::recalculate(const StyleCalculationParameters& parameters) { paints.removeExpiredTransitions(parameters.now); diff --git a/src/mbgl/layer/line_layer.hpp b/src/mbgl/layer/line_layer.hpp index d9cf73521..5f8c0f2b3 100644 --- a/src/mbgl/layer/line_layer.hpp +++ b/src/mbgl/layer/line_layer.hpp @@ -3,11 +3,16 @@ #include #include +#include +#include namespace mbgl { class LineLayer : public StyleLayer { public: + void parseLayout(const JSVal&) override; + void parsePaints(const JSVal&) override; + void recalculate(const StyleCalculationParameters&) override; LinePaintProperties properties; diff --git a/src/mbgl/layer/raster_layer.cpp b/src/mbgl/layer/raster_layer.cpp index b217f9d35..3f210233a 100644 --- a/src/mbgl/layer/raster_layer.cpp +++ b/src/mbgl/layer/raster_layer.cpp @@ -1,7 +1,26 @@ #include +#include namespace mbgl { +void RasterLayer::parsePaints(const JSVal& layer) { + paints.parseEach(layer, [&] (ClassProperties& paint, const JSVal& value) { + parseProperty>("raster-opacity", PropertyKey::RasterOpacity, paint, value); + parseProperty("raster-opacity-transition", PropertyKey::RasterOpacity, paint, value); + parseProperty>("raster-hue-rotate", PropertyKey::RasterHueRotate, paint, value); + parseProperty("raster-hue-rotate-transition", PropertyKey::RasterHueRotate, paint, value); + parseProperty>("raster-brightness-min", PropertyKey::RasterBrightnessLow, paint, value); + parseProperty>("raster-brightness-max", PropertyKey::RasterBrightnessHigh, paint, value); + parseProperty("raster-brightness-transition", PropertyKey::RasterBrightness, paint, value); + parseProperty>("raster-saturation", PropertyKey::RasterSaturation, paint, value); + parseProperty("raster-saturation-transition", PropertyKey::RasterSaturation, paint, value); + parseProperty>("raster-contrast", PropertyKey::RasterContrast, paint, value); + parseProperty("raster-contrast-transition", PropertyKey::RasterContrast, paint, value); + parseProperty>("raster-fade-duration", PropertyKey::RasterFade, paint, value); + parseProperty("raster-fade-duration-transition", PropertyKey::RasterFade, paint, value); + }); +} + void RasterLayer::recalculate(const StyleCalculationParameters& parameters) { paints.removeExpiredTransitions(parameters.now); diff --git a/src/mbgl/layer/raster_layer.hpp b/src/mbgl/layer/raster_layer.hpp index ba34dc60c..6a0de8a38 100644 --- a/src/mbgl/layer/raster_layer.hpp +++ b/src/mbgl/layer/raster_layer.hpp @@ -3,11 +3,16 @@ #include #include +#include +#include namespace mbgl { class RasterLayer : public StyleLayer { public: + void parseLayout(const JSVal&) override {}; + void parsePaints(const JSVal&) override; + void recalculate(const StyleCalculationParameters&) override; RasterPaintProperties properties; diff --git a/src/mbgl/layer/symbol_layer.cpp b/src/mbgl/layer/symbol_layer.cpp index 940c51843..983b1f370 100644 --- a/src/mbgl/layer/symbol_layer.cpp +++ b/src/mbgl/layer/symbol_layer.cpp @@ -1,9 +1,80 @@ #include #include #include +#include namespace mbgl { +void SymbolLayer::parseLayout(const JSVal& value) { + parseProperty>("symbol-placement", PropertyKey::SymbolPlacement, bucket->layout, value); + parseProperty>("symbol-spacing", PropertyKey::SymbolSpacing, bucket->layout, value); + parseProperty>("symbol-avoid-edges", PropertyKey::SymbolAvoidEdges, bucket->layout, value); + parseProperty>("icon-allow-overlap", PropertyKey::IconAllowOverlap, bucket->layout, value); + parseProperty>("icon-ignore-placement", PropertyKey::IconIgnorePlacement, bucket->layout, value); + parseProperty>("icon-optional", PropertyKey::IconOptional, bucket->layout, value); + parseProperty>("icon-rotation-alignment", PropertyKey::IconRotationAlignment, bucket->layout, value); + parseProperty>("icon-size", PropertyKey::IconSize, bucket->layout, value); + parseProperty>("icon-image", PropertyKey::IconImage, bucket->layout, value); + parseProperty>("icon-rotate", PropertyKey::IconRotate, bucket->layout, value); + parseProperty>("icon-padding", PropertyKey::IconPadding, bucket->layout, value); + parseProperty>("icon-keep-upright", PropertyKey::IconKeepUpright, bucket->layout, value); + parseProperty>>("icon-offset", PropertyKey::IconOffset, bucket->layout, value); + parseProperty>("text-rotation-alignment", PropertyKey::TextRotationAlignment, bucket->layout, value); + parseProperty>("text-field", PropertyKey::TextField, bucket->layout, value); + parseProperty>("text-font", PropertyKey::TextFont, bucket->layout, value); + parseProperty>("text-size", PropertyKey::TextSize, bucket->layout, value); + parseProperty>("text-max-width", PropertyKey::TextMaxWidth, bucket->layout, value); + parseProperty>("text-line-height", PropertyKey::TextLineHeight, bucket->layout, value); + parseProperty>("text-letter-spacing", PropertyKey::TextLetterSpacing, bucket->layout, value); + parseProperty>("text-justify", PropertyKey::TextJustify, bucket->layout, value); + parseProperty>("text-anchor", PropertyKey::TextAnchor, bucket->layout, value); + parseProperty>("text-max-angle", PropertyKey::TextMaxAngle, bucket->layout, value); + parseProperty>("text-rotate", PropertyKey::TextRotate, bucket->layout, value); + parseProperty>("text-padding", PropertyKey::TextPadding, bucket->layout, value); + parseProperty>("text-keep-upright", PropertyKey::TextKeepUpright, bucket->layout, value); + parseProperty>("text-transform", PropertyKey::TextTransform, bucket->layout, value); + parseProperty>>("text-offset", PropertyKey::TextOffset, bucket->layout, value); + parseProperty>("text-allow-overlap", PropertyKey::TextAllowOverlap, bucket->layout, value); + parseProperty>("text-ignore-placement", PropertyKey::TextIgnorePlacement, bucket->layout, value); + parseProperty>("text-optional", PropertyKey::TextOptional, bucket->layout, value); +} + +void SymbolLayer::parsePaints(const JSVal& layer) { + paints.parseEach(layer, [&] (ClassProperties& paint, const JSVal& value) { + parseProperty>("icon-opacity", PropertyKey::IconOpacity, paint, value); + parseProperty("icon-opacity-transition", PropertyKey::IconOpacity, paint, value); + parseProperty>("icon-size", PropertyKey::IconSize, paint, value); + parseProperty("icon-size-transition", PropertyKey::IconSize, paint, value); + parseProperty>("icon-color", PropertyKey::IconColor, paint, value); + parseProperty("icon-color-transition", PropertyKey::IconColor, paint, value); + parseProperty>("icon-halo-color", PropertyKey::IconHaloColor, paint, value); + parseProperty("icon-halo-color-transition", PropertyKey::IconHaloColor, paint, value); + parseProperty>("icon-halo-width", PropertyKey::IconHaloWidth, paint, value); + parseProperty("icon-halo-width-transition", PropertyKey::IconHaloWidth, paint, value); + parseProperty>("icon-halo-blur", PropertyKey::IconHaloBlur, paint, value); + parseProperty("icon-halo-blur-transition", PropertyKey::IconHaloBlur, paint, value); + parseProperty>>("icon-translate", PropertyKey::IconTranslate, paint, value); + parseProperty("icon-translate-transition", PropertyKey::IconTranslate, paint, value); + parseProperty>("icon-translate-anchor", PropertyKey::IconTranslateAnchor, paint, value); + + parseProperty>("text-opacity", PropertyKey::TextOpacity, paint, value); + parseProperty("text-opacity-transition", PropertyKey::TextOpacity, paint, value); + parseProperty>("text-size", PropertyKey::TextSize, paint, value); + parseProperty("text-size-transition", PropertyKey::TextSize, paint, value); + parseProperty>("text-color", PropertyKey::TextColor, paint, value); + parseProperty("text-color-transition", PropertyKey::TextColor, paint, value); + parseProperty>("text-halo-color", PropertyKey::TextHaloColor, paint, value); + parseProperty("text-halo-color-transition", PropertyKey::TextHaloColor, paint, value); + parseProperty>("text-halo-width", PropertyKey::TextHaloWidth, paint, value); + parseProperty("text-halo-width-transition", PropertyKey::TextHaloWidth, paint, value); + parseProperty>("text-halo-blur", PropertyKey::TextHaloBlur, paint, value); + parseProperty("text-halo-blur-transition", PropertyKey::TextHaloBlur, paint, value); + parseProperty>>("text-translate", PropertyKey::TextTranslate, paint, value); + parseProperty("text-translate-transition", PropertyKey::TextTranslate, paint, value); + parseProperty>("text-translate-anchor", PropertyKey::TextTranslateAnchor, paint, value); + }); +} + void SymbolLayer::recalculate(const StyleCalculationParameters& parameters) { paints.removeExpiredTransitions(parameters.now); diff --git a/src/mbgl/layer/symbol_layer.hpp b/src/mbgl/layer/symbol_layer.hpp index 52aed14a1..ce92c1959 100644 --- a/src/mbgl/layer/symbol_layer.hpp +++ b/src/mbgl/layer/symbol_layer.hpp @@ -3,11 +3,16 @@ #include #include +#include +#include namespace mbgl { class SymbolLayer : public StyleLayer { public: + void parseLayout(const JSVal&) override; + void parsePaints(const JSVal&) override; + void recalculate(const StyleCalculationParameters&) override; SymbolPaintProperties properties; diff --git a/src/mbgl/style/paint_properties_map.cpp b/src/mbgl/style/paint_properties_map.cpp index 18f686745..44513908c 100644 --- a/src/mbgl/style/paint_properties_map.cpp +++ b/src/mbgl/style/paint_properties_map.cpp @@ -5,6 +5,18 @@ namespace mbgl { +void PaintPropertiesMap::parseEach(const JSVal& layer, std::function parsePaint) { + rapidjson::Value::ConstMemberIterator itr = layer.MemberBegin(); + for (; itr != layer.MemberEnd(); ++itr) { + const std::string name { itr->name.GetString(), itr->name.GetStringLength() }; + if (name == "paint") { + parsePaint(paints[ClassID::Default], itr->value); + } else if (name.compare(0, 6, "paint.") == 0 && name.length() > 6) { + parsePaint(paints[ClassDictionary::Get().lookup(name.substr(6))], itr->value); + } + } +} + void PaintPropertiesMap::cascade(const std::vector& classes, const TimePoint& now, const PropertyTransition& defaultTransition) { diff --git a/src/mbgl/style/paint_properties_map.hpp b/src/mbgl/style/paint_properties_map.hpp index 14ed33fe4..26fd8301b 100644 --- a/src/mbgl/style/paint_properties_map.hpp +++ b/src/mbgl/style/paint_properties_map.hpp @@ -8,14 +8,19 @@ #include +#include + #include #include +#include namespace mbgl { class ClassProperties; class PropertyTransition; +using JSVal = rapidjson::Value; + class PaintPropertiesMap { public: void cascade(const std::vector& classNames, @@ -25,6 +30,9 @@ public: bool hasTransitions() const; void removeExpiredTransitions(const TimePoint& now); + // Call the function for each "paint" or "paint.*" object in the layer. + void parseEach(const JSVal& layer, std::function); + template void calculate(PropertyKey key, T& target, const StyleCalculationParameters& parameters) { auto it = appliedStyle.find(key); diff --git a/src/mbgl/style/piecewisefunction_properties.hpp b/src/mbgl/style/piecewisefunction_properties.hpp index d3a954395..491442958 100644 --- a/src/mbgl/style/piecewisefunction_properties.hpp +++ b/src/mbgl/style/piecewisefunction_properties.hpp @@ -18,9 +18,9 @@ struct PiecewiseConstantFunction { duration(duration_) { } - PiecewiseConstantFunction(T& value, mapbox::util::optional duration_) + PiecewiseConstantFunction(const T& value) : values({{ 0, value }}), - duration(duration_) { + duration() { } T evaluate(const StyleCalculationParameters&) const; diff --git a/src/mbgl/style/property_parsing.cpp b/src/mbgl/style/property_parsing.cpp new file mode 100644 index 000000000..ba69ed868 --- /dev/null +++ b/src/mbgl/style/property_parsing.cpp @@ -0,0 +1,434 @@ +#include + +#include + +#include + +namespace mbgl { +namespace detail { + +optional> parseFloatArray(const JSVal& value) { + if (!value.IsArray()) { + Log::Warning(Event::ParseStyle, "dasharray value must be an array of numbers"); + return {}; + } + + std::vector result; + + for (rapidjson::SizeType i = 0; i < value.Size(); ++i) { + const JSVal& part = value[i]; + + if (!part.IsNumber()) { + Log::Warning(Event::ParseStyle, "dasharray value must be an array of numbers"); + return {}; + } + + result.push_back(part.GetDouble()); + } + + return result; +} + + +template <> +optional parseProperty(const char* name, const JSVal& value) { + if (!value.IsBool()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a boolean", name); + return {}; + } + + return value.GetBool(); +} + +template <> +optional parseProperty(const char* name, const JSVal& value) { + if (!value.IsNumber()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a number, or a number function", name); + return {}; + } + + return value.GetDouble(); +} + +template <> +optional parseProperty(const char* name, const JSVal& value) { + if (std::string { "text-font" } == name) { + if (!value.IsArray()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be an array of strings", name); + return {}; + } + + std::string result = ""; + for (rapidjson::SizeType i = 0; i < value.Size(); ++i) { + const JSVal& stop = value[i]; + if (stop.IsString()) { + result += stop.GetString(); + if (i < value.Size()-1) { + result += ","; + } + } else { + Log::Warning(Event::ParseStyle, "text-font members must be strings"); + return {}; + } + } + return result; + } + + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name); + return {}; + } + + return std::string { value.GetString(), value.GetStringLength() }; +} + +template <> +optional parseProperty(const char* name, const JSVal& value) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name); + return {}; + } + + CSSColorParser::Color css_color = CSSColorParser::parse({ value.GetString(), value.GetStringLength() }); + + // Premultiply the color. + const float factor = css_color.a / 255; + + return Color{{(float)css_color.r * factor, + (float)css_color.g * factor, + (float)css_color.b * factor, + css_color.a}}; +} + +template <> +optional parseProperty(const char* name, const JSVal& value) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name); + return {}; + } + + return { TranslateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template <> +optional parseProperty(const char* name, const JSVal& value) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name); + return {}; + } + + return { RotateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template <> +optional parseProperty(const char* name, const JSVal& value) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name); + return {}; + } + + return { CapTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template <> +optional parseProperty(const char* name, const JSVal& value) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name); + return {}; + } + + return { JoinTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template <> +optional parseProperty(const char* name, const JSVal& value) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name); + return {}; + } + + return { PlacementTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template <> +optional parseProperty(const char* name, const JSVal& value) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name); + return {}; + } + + return { TextAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template <> +optional parseProperty(const char* name, const JSVal& value) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name); + return {}; + } + + return { TextJustifyTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template <> +optional parseProperty(const char* name, const JSVal& value) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name); + return {}; + } + + return { TextTransformTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template <> +optional parseProperty(const char* name, const JSVal& value) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name); + return {}; + } + + return { RotationAlignmentTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template <> +optional> parseProperty(const char* name, const JSVal& value) { + if (value.IsArray() && value.Size() == 2 && + value[rapidjson::SizeType(0)].IsNumber() && + value[rapidjson::SizeType(1)].IsNumber()) { + + float first = value[rapidjson::SizeType(0)].GetDouble(); + float second = value[rapidjson::SizeType(1)].GetDouble(); + return { {{ first, second }} }; + } else { + Log::Warning(Event::ParseStyle, "value of '%s' must be an array of two numbers", name); + return {}; + } +} + +template <> +optional parseProperty(const char *, const JSVal& value) { + PropertyTransition transition; + if (value.IsObject()) { + bool parsed = false; + if (value.HasMember("duration") && value["duration"].IsNumber()) { + transition.duration = std::chrono::milliseconds(value["duration"].GetUint()); + parsed = true; + } + if (value.HasMember("delay") && value["delay"].IsNumber()) { + transition.delay = std::chrono::milliseconds(value["delay"].GetUint()); + parsed = true; + } + if (!parsed) { + return {}; + } + } + return transition; +} + +// --- Function --- + +template +optional>> parseStops(const char* name, const JSVal& value) { + if (!value.IsArray()) { + Log::Warning(Event::ParseStyle, "stops function must specify a stops array"); + return {}; + } + + std::vector> stops; + + for (rapidjson::SizeType i = 0; i < value.Size(); ++i) { + const JSVal& stop = value[i]; + + if (!stop.IsArray()) { + Log::Warning(Event::ParseStyle, "function argument must be a numeric value"); + return {}; + } + + if (stop.Size() != 2) { + Log::Warning(Event::ParseStyle, "stop must have zoom level and value specification"); + return {}; + } + + const JSVal& z = stop[rapidjson::SizeType(0)]; + if (!z.IsNumber()) { + Log::Warning(Event::ParseStyle, "zoom level in stop must be a number"); + return {}; + } + + optional v = parseProperty(name, stop[rapidjson::SizeType(1)]); + if (!v) { + return {}; + } + + stops.emplace_back(z.GetDouble(), *v); + } + + return stops; +} + +template +optional> parseFunction(const char* name, const JSVal& value) { + if (!value.IsObject()) { + auto constant = parseProperty(name, value); + if (!constant) { + return {}; + } + return { ConstantFunction(*constant) }; + } + + if (!value.HasMember("stops")) { + Log::Warning(Event::ParseStyle, "function must specify a function type"); + return {}; + } + + float base = 1.0f; + + if (value.HasMember("base")) { + const JSVal& value_base = value["base"]; + + if (!value_base.IsNumber()) { + Log::Warning(Event::ParseStyle, "base must be numeric"); + return {}; + } + + base = value_base.GetDouble(); + } + + auto stops = parseStops(name, value["stops"]); + + if (!stops) { + return {}; + } + + return { StopsFunction(*stops, base) }; +} + +template <> optional>> parseProperty(const char* name, const JSVal& value) { + return parseFunction>(name, value); +} + +template <> optional> parseProperty(const char* name, const JSVal& value) { + return parseFunction(name, value); +} + +template <> optional> parseProperty(const char* name, const JSVal& value) { + return parseFunction(name, value); +} + +template <> optional> parseProperty(const char* name, const JSVal& value) { + return parseFunction(name, value); +} + +template <> optional> parseProperty(const char* name, const JSVal& value) { + return parseFunction(name, value); +} + +template <> optional> parseProperty(const char* name, const JSVal& value) { + return parseFunction(name, value); +} + +template <> optional> parseProperty(const char* name, const JSVal& value) { + return parseFunction(name, value); +} + +template <> optional> parseProperty(const char* name, const JSVal& value) { + return parseFunction(name, value); +} + +template <> optional> parseProperty(const char* name, const JSVal& value) { + return parseFunction(name, value); +} + +template <> optional> parseProperty(const char* name, const JSVal& value) { + return parseFunction(name, value); +} + +template <> optional> parseProperty(const char* name, const JSVal& value) { + return parseFunction(name, value); +} + +template <> optional> parseProperty(const char* name, const JSVal& value) { + return parseFunction(name, value); +} + +template<> optional> parseProperty(const char* name, const JSVal& value) { + return parseFunction(name, value); +} + +template<> optional> parseProperty(const char* name, const JSVal& value) { + return parseFunction(name, value); +} + +template +optional> parsePiecewiseConstantFunction(const JSVal& value, const JSVal& transition) { + mapbox::util::optional duration; + if (transition.HasMember("duration")) { + duration = std::chrono::milliseconds(transition["duration"].GetUint()); + } + + if (!value.HasMember("stops")) { + Log::Warning(Event::ParseStyle, "function must specify a function type"); + return {}; + } + + auto stops = parseStops("", value["stops"]); + + if (!stops) { + return {}; + } + + return PiecewiseConstantFunction(*stops, duration); +} + +template <> +optional>> parseProperty(const char*, const JSVal& value) { + auto floatarray = parseFloatArray(value); + if (!floatarray) { + return {}; + } + + Faded> parsed; + parsed.to = *floatarray; + return parsed; +} + +template <> +optional> parseProperty(const char* name, const JSVal& value) { + if (!value.IsString()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a string, or a string function", name); + return {}; + } + + Faded parsed; + parsed.to = { value.GetString(), value.GetStringLength() }; + return parsed; +} + +template <> +optional>>> parseProperty(const char* name, const JSVal& value, const JSVal& transition) { + if (value.IsObject()) { + return parsePiecewiseConstantFunction>>(value, transition); + } + + auto constant = parseProperty>>(name, value); + if (!constant) { + return {}; + } + return PiecewiseConstantFunction>>(*constant); +} + +template <> +optional>> parseProperty(const char* name, const JSVal& value, const JSVal& transition) { + if (value.IsObject()) { + return parsePiecewiseConstantFunction>(value, transition); + } + + auto constant = parseProperty>(name, value); + if (!constant) { + return {}; + } + return PiecewiseConstantFunction>(*constant); +} + +} +} diff --git a/src/mbgl/style/property_parsing.hpp b/src/mbgl/style/property_parsing.hpp new file mode 100644 index 000000000..478e19289 --- /dev/null +++ b/src/mbgl/style/property_parsing.hpp @@ -0,0 +1,57 @@ +#ifndef MBGL_PROPERTY_PARSING +#define MBGL_PROPERTY_PARSING + +#include + +#include +#include + +namespace mbgl { + +using JSVal = rapidjson::Value; + +template +using optional = mapbox::util::optional; + +namespace detail { + +template +optional parseProperty(const char* name, const JSVal&); + +template +optional parseProperty(const char* name, const JSVal&, const JSVal& transition); + +} + +template +void parseProperty(const char* name, PropertyKey key, ClassProperties& properties, const JSVal& value) { + if (!value.HasMember(name)) + return; + + const optional res = detail::parseProperty(name, value[name]); + + if (res) { + properties.set(key, *res); + } +} + +template +void parseProperty(const char* name, PropertyKey key, ClassProperties& properties, const JSVal& value, const char* transitionName) { + if (!value.HasMember(name)) + return; + + const JSVal& noTransition = JSVal { rapidjson::kObjectType }; + + const optional res = detail::parseProperty(name, value[name], + value.HasMember(transitionName) + ? value[transitionName] + : noTransition); + + if (res) { + properties.set(key, *res); + } +} + +} + +#endif diff --git a/src/mbgl/style/style_layer.hpp b/src/mbgl/style/style_layer.hpp index 43d595bb3..a27e8a89c 100644 --- a/src/mbgl/style/style_layer.hpp +++ b/src/mbgl/style/style_layer.hpp @@ -1,8 +1,7 @@ #ifndef MBGL_STYLE_STYLE_LAYER #define MBGL_STYLE_STYLE_LAYER -#include -#include +#include #include #include @@ -11,9 +10,10 @@ #include #include +#include + #include #include -#include namespace mbgl { @@ -21,12 +21,17 @@ class StyleBucket; class StyleCalculationParameters; class PropertyTransition; +using JSVal = rapidjson::Value; + class StyleLayer : public util::noncopyable { public: static std::unique_ptr create(StyleLayerType); virtual ~StyleLayer() = default; + virtual void parseLayout(const JSVal& value) = 0; + virtual void parsePaints(const JSVal& value) = 0; + // Partially evaluate paint properties based on a set of classes. void cascade(const std::vector& classNames, const TimePoint& now, diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index 3aecfa51e..c7b682747 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -1,26 +1,13 @@ #include -#include #include -#include -#include -#include -#include -#include -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wunknown-pragmas" -#pragma GCC diagnostic ignored "-Wunused-local-typedefs" -#include -#pragma GCC diagnostic pop +#include #include namespace mbgl { -using JSVal = const rapidjson::Value&; - -void StyleParser::parse(JSVal document) { +void StyleParser::parse(const JSVal& document) { if (document.HasMember("version")) { version = document["version"].GetInt(); if (version != 8) { @@ -45,583 +32,70 @@ void StyleParser::parse(JSVal document) { } } -#pragma mark - Parse Render Properties - -template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, bool &target, const char *name) { - if (value.HasMember(name)) { - JSVal property = value[name]; - if (property.IsBool()) { - target = property.GetBool(); - return StyleParserSuccess; - } else { - Log::Warning(Event::ParseStyle, "'%s' must be a boolean", name); - } - } - return StyleParserFailure; -} - - -template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, std::string &target, const char *name) { - if (value.HasMember(name)) { - JSVal property = value[name]; - if (property.IsString()) { - target = { property.GetString(), property.GetStringLength() }; - return StyleParserSuccess; - } else { - Log::Warning(Event::ParseStyle, "'%s' must be a string", name); - } - } - return StyleParserFailure; -} - -template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, float &target, const char *name) { - if (value.HasMember(name)) { - JSVal property = value[name]; - if (property.IsNumber()) { - target = property.GetDouble(); - return StyleParserSuccess; - } else { - Log::Warning(Event::ParseStyle, "'%s' must be a number", name); - } - } - return StyleParserFailure; -} - -template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, uint16_t &target, const char *name) { - if (value.HasMember(name)) { - JSVal property = value[name]; - if (property.IsUint()) { - unsigned int int_value = property.GetUint(); - if (int_value > std::numeric_limits::max()) { - Log::Warning(Event::ParseStyle, "values for %s that are larger than %d are not supported", name, std::numeric_limits::max()); - return StyleParserFailure; - } - - target = int_value; - return StyleParserSuccess; - } else { - Log::Warning(Event::ParseStyle, "%s must be an unsigned integer", name); - } - } - return StyleParserFailure; -} - -template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, int32_t &target, const char *name) { - if (value.HasMember(name)) { - JSVal property = value[name]; - if (property.IsInt()) { - target = property.GetInt(); - return StyleParserSuccess; - } else { - Log::Warning(Event::ParseStyle, "%s must be an integer", name); - } - } - return StyleParserFailure; -} - -template<> StyleParser::Status StyleParser::parseRenderProperty(JSVal value, vec2 &target, const char *name) { - if (value.HasMember(name)) { - JSVal property = value[name]; - if (property.IsArray()) { - if (property.Size() >= 2) { - target.x = property[(rapidjson::SizeType)0].GetDouble(); - target.y = property[(rapidjson::SizeType)1].GetDouble(); - return StyleParserSuccess; - } else { - Log::Warning(Event::ParseStyle, "%s must have at least two members", name); - } - } else { - Log::Warning(Event::ParseStyle, "%s must be an array of numbers", name); - } - } - return StyleParserFailure; -} - -template -StyleParser::Status StyleParser::parseRenderProperty(JSVal value, T &target, const char *name) { - if (value.HasMember(name)) { - JSVal property = value[name]; - if (property.IsString()) { - target = Parser({ property.GetString(), property.GetStringLength() }); - return StyleParserSuccess; - } else { - Log::Warning(Event::ParseStyle, "%s must have one of the enum values", name); - } - } - return StyleParserFailure; -} - - -#pragma mark - Parse Sources - -void StyleParser::parseSources(JSVal value) { - if (value.IsObject()) { - rapidjson::Value::ConstMemberIterator itr = value.MemberBegin(); - for (; itr != value.MemberEnd(); ++itr) { - std::string name { itr->name.GetString(), itr->name.GetStringLength() }; - std::unique_ptr source = std::make_unique(); - parseRenderProperty(itr->value, source->info.type, "type"); - parseRenderProperty(itr->value, source->info.url, "url"); - parseRenderProperty(itr->value, source->info.tile_size, "tileSize"); - source->info.source_id = name; - source->info.parseTileJSONProperties(itr->value); - sourcesMap.emplace(name, source.get()); - sources.emplace_back(std::move(source)); - } - } else { - Log::Warning(Event::ParseStyle, "sources must be an object"); - } -} - -#pragma mark - Parse Style Properties - -Color parseColor(JSVal value) { - if (!value.IsString()) { - Log::Warning(Event::ParseStyle, "color value must be a string"); - return Color{{ 0, 0, 0, 0 }}; - } - - CSSColorParser::Color css_color = CSSColorParser::parse({ value.GetString(), value.GetStringLength() }); - - // Premultiply the color. - const float factor = css_color.a / 255; - - return Color{{(float)css_color.r * factor, - (float)css_color.g * factor, - (float)css_color.b * factor, - css_color.a}}; -} - -StyleParser::Result> StyleParser::parseFloatArray(JSVal value) { - if (!value.IsArray()) { - Log::Warning(Event::ParseStyle, "dasharray value must be an array of numbers"); - return Result> { StyleParserFailure, std::vector() }; - } - - std::vector vec; - for (rapidjson::SizeType i = 0; i < value.Size(); ++i) { - JSVal part = value[i]; - if (!part.IsNumber()) { - Log::Warning(Event::ParseStyle, "dasharray value must be an array of numbers"); - return Result> { StyleParserFailure, std::vector() }; - } - vec.push_back(part.GetDouble()); - } - return Result> { StyleParserSuccess, vec }; -} - -template <> -StyleParser::Result> StyleParser::parseProperty(JSVal value, const char*) { - if (value.IsArray() && value.Size() == 2 && - value[rapidjson::SizeType(0)].IsNumber() && - value[rapidjson::SizeType(1)].IsNumber()) { - - float first = value[rapidjson::SizeType(0)].GetDouble(); - float second = value[rapidjson::SizeType(1)].GetDouble(); - return Result> { StyleParserSuccess, {{ first, second }} }; - } else { - Log::Warning(Event::ParseStyle, "value must be array of two numbers"); - return Result> { StyleParserFailure, {{ 0.0f, 0.0f }} }; - } -} - -template <> -StyleParser::Result StyleParser::parseProperty(JSVal value, const char* property_name) { - if (value.IsNumber()) { - return Result { StyleParserSuccess, value.GetDouble() }; - } else { - Log::Warning(Event::ParseStyle, "value of '%s' must be a number, or a number function", property_name); - return Result { StyleParserFailure, 0.0f }; - } -} - -template <> -StyleParser::Result StyleParser::parseProperty(JSVal value, const char*) { - return Result { StyleParserSuccess, parseColor(value) }; -} - -template <> -StyleParser::Result>> StyleParser::parseProperty(JSVal value, const char*) { - Faded> parsed; - parsed.to = std::get<1>(parseFloatArray(value)); - return Result>> { StyleParserSuccess, parsed }; -} - -template <> -StyleParser::Result> StyleParser::parseProperty(JSVal value, const char *property_name) { - Faded parsed; - if (value.IsString()) { - parsed.to = { value.GetString(), value.GetStringLength() }; - return Result> { StyleParserSuccess, parsed }; - } else { - Log::Warning(Event::ParseStyle, "value of '%s' must be a string, or a string function", property_name); - return Result> { StyleParserFailure, parsed }; - } -} - -template -StyleParser::Result>> StyleParser::parseStops(JSVal value_stops, const char *property_name) { - - if (!value_stops.IsArray()) { - Log::Warning(Event::ParseStyle, "stops function must specify a stops array"); - return Result>> { StyleParserFailure, {}}; - } - - std::vector> stops; - - for (rapidjson::SizeType i = 0; i < value_stops.Size(); ++i) { - JSVal stop = value_stops[i]; - if (stop.IsArray()) { - if (stop.Size() != 2) { - Log::Warning(Event::ParseStyle, "stop must have zoom level and value specification"); - return Result>> { StyleParserFailure, {}}; - } - - JSVal z = stop[rapidjson::SizeType(0)]; - if (!z.IsNumber()) { - Log::Warning(Event::ParseStyle, "zoom level in stop must be a number"); - return Result>> { StyleParserFailure, {}}; - } - - stops.emplace_back(z.GetDouble(), std::get<1>(parseProperty(stop[rapidjson::SizeType(1)], property_name))); - } else { - Log::Warning(Event::ParseStyle, "function argument must be a numeric value"); - return Result>> { StyleParserFailure, {}}; - } - } - return Result>>(StyleParserSuccess, stops); -} - -template -StyleParser::Result> StyleParser::parseFunction(JSVal value, const char *property_name) { +void StyleParser::parseSources(const JSVal& value) { if (!value.IsObject()) { - return parseProperty(value, property_name); - } - - if (!value.HasMember("stops")) { - Log::Warning(Event::ParseStyle, "function must specify a function type"); - return Result> { StyleParserFailure, ConstantFunction(T()) }; - } - - float base = 1.0f; - - if (value.HasMember("base")) { - JSVal value_base = value["base"]; - if (value_base.IsNumber()) { - base = value_base.GetDouble(); - } else { - Log::Warning(Event::ParseStyle, "base must be numeric"); - } - } - - auto stops = parseStops(value["stops"], property_name); - - if (!std::get<0>(stops)) { - return Result> { StyleParserFailure, ConstantFunction(T()) }; - } - - return Result> { StyleParserSuccess, StopsFunction(std::get<1>(stops), base) }; -} - -template -StyleParser::Result> StyleParser::parsePiecewiseConstantFunction(JSVal value, JSVal transition) { - mapbox::util::optional duration; - if (transition.HasMember("duration")) { - duration = std::chrono::milliseconds(transition["duration"].GetUint()); - } - - if (!value.HasMember("stops")) { - Log::Warning(Event::ParseStyle, "function must specify a function type"); - return Result> { StyleParserFailure, { {}, duration } }; - } - - auto stops = parseStops(value["stops"], ""); - - if (!std::get<0>(stops)) { - return Result> { StyleParserFailure, { {}, duration } }; - } - - return Result> { StyleParserSuccess, { std::get<1>(stops), duration } }; -} - -template -StyleParser::Status StyleParser::setProperty(JSVal value, const char *property_name, PropertyKey key, ClassProperties &klass) { - auto res = parseProperty(value, property_name); - if (std::get<0>(res)) { - klass.set(key, std::get<1>(res)); - } - return std::get<0>(res); -} - -template -StyleParser::Status StyleParser::setProperty(JSVal value, const char *property_name, PropertyKey key, ClassProperties &klass, JSVal transition) { - auto res = parseProperty(value, property_name, transition); - if (std::get<0>(res)) { - klass.set(key, std::get<1>(res)); - } - return std::get<0>(res); -} - -template -void StyleParser::parseVisibility(StyleBucket &bucket, JSVal value) { - if (!value.HasMember("visibility")) { - return; - } else if (!value["visibility"].IsString()) { - Log::Warning(Event::ParseStyle, "value of 'visibility' must be a string"); - bucket.visibility = VisibilityType::Visible; + Log::Warning(Event::ParseStyle, "sources must be an object"); return; } - bucket.visibility = VisibilityTypeClass({ value["visibility"].GetString(), value["visibility"].GetStringLength() }); -} -template -StyleParser::Status StyleParser::parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) { - if (!value.HasMember(property_name)) { - return StyleParserFailure; - } else { - return setProperty(value[property_name], property_name, key, klass); - } -} + rapidjson::Value::ConstMemberIterator itr = value.MemberBegin(); + for (; itr != value.MemberEnd(); ++itr) { + const JSVal& nameVal = itr->name; + const JSVal& sourceVal = itr->value; -template -StyleParser::Status StyleParser::parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value, const char *transition_name) { - if (!value.HasMember(property_name)) { - return StyleParserFailure; - } else { - if (value.HasMember(transition_name)) { - return setProperty(value[property_name], property_name, key, klass, value[transition_name]); - } else { - JSVal val = JSVal { rapidjson::kObjectType }; - return setProperty(value[property_name], property_name, key, klass, val); + std::unique_ptr source = std::make_unique(); + + source->info.source_id = { nameVal.GetString(), nameVal.GetStringLength() }; + + if (!sourceVal.HasMember("type")) { + Log::Warning(Event::ParseStyle, "source must have a type"); + continue; } - } -} -template<> StyleParser::Result StyleParser::parseProperty(JSVal value, const char *property_name) { - if (std::string { "text-font" } == property_name) { - if (!value.IsArray()) { - Log::Warning(Event::ParseStyle, "value of '%s' must be an array of strings", property_name); - return Result { StyleParserFailure, std::string() }; - } else { - std::string result = ""; - for (rapidjson::SizeType i = 0; i < value.Size(); ++i) { - JSVal stop = value[i]; - if (stop.IsString()) { - result += stop.GetString(); - if (i < value.Size()-1) { - result += ","; - } - } else { - Log::Warning(Event::ParseStyle, "text-font members must be strings"); - return Result { StyleParserFailure, {} }; - } + const JSVal& typeVal = sourceVal["type"]; + if (!typeVal.IsString()) { + Log::Warning(Event::ParseStyle, "source type must have one of the enum values"); + continue; + } + + source->info.type = SourceTypeClass({ typeVal.GetString(), typeVal.GetStringLength() }); + + if (sourceVal.HasMember("url")) { + const JSVal& urlVal = sourceVal["url"]; + + if (!urlVal.IsString()) { + Log::Warning(Event::ParseStyle, "source url must be a string"); + continue; } - return Result { StyleParserSuccess, result }; + + source->info.url = { urlVal.GetString(), urlVal.GetStringLength() }; } - } else if (!value.IsString()) { - Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); - return Result { StyleParserFailure, std::string() }; - } else { - return Result { StyleParserSuccess, { value.GetString(), value.GetStringLength() } }; - } -} -template<> StyleParser::Result StyleParser::parseProperty(JSVal value, const char *property_name) { - if (!value.IsBool()) { - Log::Warning(Event::ParseStyle, "value of '%s' must be a boolean", property_name); - return Result { StyleParserFailure, StyleParserSuccess }; - } + if (sourceVal.HasMember("tileSize")) { + const JSVal& tileSizeVal = sourceVal["tileSize"]; - return Result { StyleParserSuccess, value.GetBool() }; -} + if (!tileSizeVal.IsUint()) { + Log::Warning(Event::ParseStyle, "source tileSize must be an unsigned integer"); + continue; + } -template<> StyleParser::Result StyleParser::parseProperty(JSVal value, const char *property_name) { - if (!value.IsString()) { - Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); - return Result { StyleParserFailure, TranslateAnchorType::Map }; - } + unsigned int intValue = tileSizeVal.GetUint(); + if (intValue > std::numeric_limits::max()) { + Log::Warning(Event::ParseStyle, "values for tileSize that are larger than %d are not supported", std::numeric_limits::max()); + continue; + } - return Result { StyleParserSuccess, TranslateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; -} - -template<> StyleParser::Result StyleParser::parseProperty(JSVal value, const char *property_name) { - if (!value.IsString()) { - Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); - return Result { StyleParserFailure, RotateAnchorType::Map }; - } - - return Result { StyleParserSuccess, RotateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; -} - -template<> StyleParser::Result StyleParser::parseProperty(JSVal value, const char *property_name) { - if (!value.IsString()) { - Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); - return Result { StyleParserFailure, CapType::Butt }; - } - - return Result { StyleParserSuccess, CapTypeClass({ value.GetString(), value.GetStringLength() }) }; -} - -template<> StyleParser::Result StyleParser::parseProperty(JSVal value, const char *property_name) { - if (!value.IsString()) { - Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); - return Result { StyleParserFailure, JoinType::Miter }; - } - - return Result { StyleParserSuccess, JoinTypeClass({ value.GetString(), value.GetStringLength() }) }; -} - -template<> StyleParser::Result StyleParser::parseProperty(JSVal value, const char *property_name) { - if (!value.IsString()) { - Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); - return Result { StyleParserFailure, PlacementType::Point }; - } - - return Result { StyleParserSuccess, PlacementTypeClass({ value.GetString(), value.GetStringLength() }) }; -} - -template<> StyleParser::Result StyleParser::parseProperty(JSVal value, const char *property_name) { - if (!value.IsString()) { - Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); - return Result { StyleParserFailure, TextAnchorType::Center }; - } - - return Result { StyleParserSuccess, TextAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; -} - -template<> StyleParser::Result StyleParser::parseProperty(JSVal value, const char *property_name) { - if (!value.IsString()) { - Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); - return Result { StyleParserFailure, TextJustifyType::Center }; - } - - return Result { StyleParserSuccess, TextJustifyTypeClass({ value.GetString(), value.GetStringLength() }) }; -} - -template<> StyleParser::Result StyleParser::parseProperty(JSVal value, const char *property_name) { - if (!value.IsString()) { - Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); - return Result { StyleParserFailure, TextTransformType::None }; - } - - return Result { StyleParserSuccess, TextTransformTypeClass({ value.GetString(), value.GetStringLength() }) }; -} - -template<> StyleParser::Result StyleParser::parseProperty(JSVal value, const char *property_name) { - if (!value.IsString()) { - Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); - return Result { StyleParserFailure, RotationAlignmentType::Map }; - } - - return Result { StyleParserSuccess, RotationAlignmentTypeClass({ value.GetString(), value.GetStringLength() }) }; -} - -template<> StyleParser::Result StyleParser::parseProperty(JSVal value, const char */*property_name*/) { - PropertyTransition transition; - if (value.IsObject()) { - bool parsed = false; - if (value.HasMember("duration") && value["duration"].IsNumber()) { - transition.duration = std::chrono::milliseconds(value["duration"].GetUint()); - parsed = true; + source->info.tile_size = intValue; } - if (value.HasMember("delay") && value["delay"].IsNumber()) { - transition.delay = std::chrono::milliseconds(value["delay"].GetUint()); - parsed = true; - } - if (!parsed) { - return Result { StyleParserFailure, std::move(transition) }; - } - } - return Result { StyleParserSuccess, std::move(transition) }; -} -template<> StyleParser::Result>> StyleParser::parseProperty(JSVal value, const char *property_name) { - return parseFunction>(value, property_name); -} + source->info.parseTileJSONProperties(sourceVal); -template<> StyleParser::Result> StyleParser::parseProperty(JSVal value, const char *property_name) { - return parseFunction(value, property_name); -} - -template<> StyleParser::Result> StyleParser::parseProperty(JSVal value, const char *property_name) { - return parseFunction(value, property_name); -} - -template<> StyleParser::Result> StyleParser::parseProperty(JSVal value, const char *property_name) { - return parseFunction(value, property_name); -} - -template<> StyleParser::Result> StyleParser::parseProperty(JSVal value, const char *property_name) { - return parseFunction(value, property_name); -} - -template<> StyleParser::Result> StyleParser::parseProperty(JSVal value, const char *property_name) { - return parseFunction(value, property_name); -} - -template<> StyleParser::Result> StyleParser::parseProperty(JSVal value, const char *property_name) { - return parseFunction(value, property_name); -} - -template<> StyleParser::Result> StyleParser::parseProperty(JSVal value, const char *property_name) { - return parseFunction(value, property_name); -} - -template<> StyleParser::Result> StyleParser::parseProperty(JSVal value, const char *property_name) { - return parseFunction(value, property_name); -} - -template<> StyleParser::Result> StyleParser::parseProperty(JSVal value, const char *property_name) { - return parseFunction(value, property_name); -} - -template<> StyleParser::Result> StyleParser::parseProperty(JSVal value, const char *property_name) { - return parseFunction(value, property_name); -} - - -template<> StyleParser::Result> StyleParser::parseProperty(JSVal value, const char *property_name) { - return parseFunction(value, property_name); -} - -template<> StyleParser::Result> StyleParser::parseProperty(JSVal value, const char *property_name) { - return parseFunction(value, property_name); -} - -template<> StyleParser::Result> StyleParser::parseProperty(JSVal value, const char *property_name) { - return parseFunction(value, property_name); -} - -template<> StyleParser::Result>>> StyleParser::parseProperty(JSVal value, const char *property_name, JSVal transition) { - if (value.IsObject()) { - return parsePiecewiseConstantFunction>>(value, transition); - } else if (value.IsArray()) { - Faded> parsed; - Result> floatarray = parseFloatArray(value); - parsed.to = std::get<1>(floatarray); - return Result>>> { std::get<0>(floatarray), { parsed, {} } }; - } else { - Log::Warning(Event::ParseStyle, "value of '%s' must be an array of numbers, or a number array function", property_name); - return Result>>> { StyleParserFailure, { {}, {} } }; + sourcesMap.emplace(source->info.source_id, source.get()); + sources.emplace_back(std::move(source)); } } -template<> StyleParser::Result>> StyleParser::parseProperty(JSVal value, const char *property_name, JSVal transition) { - if (value.IsObject()) { - return parsePiecewiseConstantFunction>(value, transition); - } else if (value.IsString()) { - Faded parsed; - parsed.to = { value.GetString(), value.GetStringLength() }; - return Result>> { StyleParserSuccess, { parsed, {} } }; - } else { - Log::Warning(Event::ParseStyle, "value of '%s' must be string or a string function", property_name); - return Result>> { StyleParserFailure, { {}, {} } }; - } -} - -#pragma mark - Parse Layers - -void StyleParser::parseLayers(JSVal value) { +void StyleParser::parseLayers(const JSVal& value) { std::vector ids; if (!value.IsArray()) { @@ -630,7 +104,7 @@ void StyleParser::parseLayers(JSVal value) { } for (rapidjson::SizeType i = 0; i < value.Size(); ++i) { - JSVal layerValue = value[i]; + const JSVal& layerValue = value[i]; if (!layerValue.IsObject()) { Log::Warning(Event::ParseStyle, "layer must be an object"); @@ -642,7 +116,7 @@ void StyleParser::parseLayers(JSVal value) { continue; } - JSVal id = layerValue["id"]; + const JSVal& id = layerValue["id"]; if (!id.IsString()) { Log::Warning(Event::ParseStyle, "layer id must be a string"); continue; @@ -654,7 +128,7 @@ void StyleParser::parseLayers(JSVal value) { continue; } - layersMap.emplace(layerID, std::pair> { layerValue, nullptr }); + layersMap.emplace(layerID, std::pair> { layerValue, nullptr }); ids.push_back(layerID); } @@ -671,7 +145,7 @@ void StyleParser::parseLayers(JSVal value) { } } -void StyleParser::parseLayer(const std::string& id, JSVal value, util::ptr& layer) { +void StyleParser::parseLayer(const std::string& id, const JSVal& value, util::ptr& layer) { if (layer) { // Skip parsing this again. We already have a valid layer definition. return; @@ -685,7 +159,7 @@ void StyleParser::parseLayer(const std::string& id, JSVal value, util::ptr