refactor functions

This commit is contained in:
Konstantin Käfer
2014-06-30 16:52:39 +02:00
parent 93cd4c5c7b
commit 18d898fae7
14 changed files with 615 additions and 396 deletions

View File

@@ -6,7 +6,7 @@ module.exports = {
"@land": "#eee",
"@water": "#999",
"@park": "#bda",
"@road": "#fefefe",
"@road": "#FF00FF",
"@border": "#6d90ab",
"@building": "#ddd",
"@building_outline": "#ccc",
@@ -53,28 +53,26 @@ module.exports = {
"fill-color": "blue"
}
}, {
"id": "roads",
"layers": [{
"id": "road",
"source": "mapbox.mapbox-streets-v5",
"source-layer": "road",
"render": {
"type": "line",
"line-cap": "round",
"line-join": "bevel"
},
"style": {
"line-color": "@road",
"line-blur": "@road_blur",
"line-width": {
"fn": "exponential",
"z": 10,
"val": -1,
"slope": 0.2,
"min": 1
}
"id": "road",
"source": "mapbox.mapbox-streets-v5",
"source-layer": "road",
"render": {
"type": "line",
"line-cap": "round",
"line-join": "bevel"
},
"style": {
"line-color": "@road",
"line-blur": "@road_blur",
"line-width": {
"fn": "exponential",
"z": 10,
"val": -1,
"slope": 0.2,
"min": 1,
"max": 1000
}
}]
}
}, {
"id": "building",
"source": "mapbox.mapbox-streets-v5",

View File

@@ -1,43 +1,72 @@
#ifndef LLMR_STYLE_FUNCTION_PROPERTIES
#define LLMR_STYLE_FUNCTION_PROPERTIES
#include <llmr/util/variant.hpp>
#include <vector>
namespace llmr {
namespace functions {
template <typename T>
struct ConstantFunction {
inline ConstantFunction(const T &value) : value(value) {}
inline T evaluate(float) const { return value; }
float null(float z, const std::vector<float>&);
float constant(float z, const std::vector<float>& values);
float min(float z, const std::vector<float>& values);
float max(float z, const std::vector<float>& values);
float stops(float z, const std::vector<float>& values);
float linear(float z, const std::vector<float>& values);
float exponential(float z, const std::vector<float>& values);
private:
const T value;
};
}
template <typename T>
struct LinearFunction {
inline LinearFunction(const T &value, float z_base, float slope, const T &min, const T &max)
: value(value), min(min), max(max), z_base(z_base), slope(slope) {}
T evaluate(float z) const;
struct FunctionProperty {
typedef float (*fn)(float z, const std::vector<float>& values);
private:
const T value, min, max;
const float z_base, slope;
};
fn function;
std::vector<float> values;
template <typename T>
struct ExponentialFunction {
inline ExponentialFunction(const T &value, float z_base, float slope, const T &min,
const T &max)
: value(value), min(min), max(max), z_base(z_base), slope(slope) {}
T evaluate(float z) const;
inline FunctionProperty() : function(&functions::null) {}
private:
const T value, min, max;
const float z_base, slope;
};
inline FunctionProperty(const FunctionProperty &property)
: function(property.function), values(property.values) {}
inline FunctionProperty(FunctionProperty &&property)
: function(property.function), values(std::move(property.values)) {}
template <typename T>
struct StopsFunction {
inline StopsFunction(const std::vector<std::pair<float, T>> &values) : values(values) {}
T evaluate(float z) const;
private:
const std::vector<std::pair<float, T>> values;
};
inline void operator=(const FunctionProperty &rhs) {
function = rhs.function;
values = rhs.values;
template <typename T>
using Function = util::variant<
ConstantFunction<T>,
LinearFunction<T>,
ExponentialFunction<T>,
StopsFunction<T>
>;
template <typename T>
struct FunctionEvaluator {
typedef T result_type;
inline FunctionEvaluator(float z) : z(z) {}
template <template <typename> class Fn>
inline result_type operator()(const Fn<T>& fn) {
return fn.evaluate(z);
}
inline FunctionProperty(float value) : function(&functions::constant), values(1, value) {}
template <typename T> inline T evaluate(float z) const { return function(z, values); }
private:
float z;
};
}

View File

@@ -8,12 +8,15 @@
namespace llmr {
typedef util::variant<
FunctionProperty,
TranslateAnchorType,
RotateAnchorType,
bool,
float,
Color,
std::string,
bool
TranslateAnchorType,
RotateAnchorType,
Function<bool>,
Function<float>,
Function<Color>
> PropertyValue;
}

View File

@@ -15,10 +15,10 @@ struct PrerenderProperties {
class RasterizeProperties {
public:
boost::optional<FunctionProperty> enabled;
boost::optional<FunctionProperty> buffer;
boost::optional<FunctionProperty> size;
boost::optional<FunctionProperty> blur;
boost::optional<Function<bool>> enabled;
boost::optional<Function<float>> buffer;
boost::optional<Function<float>> size;
boost::optional<Function<float>> blur;
};
}

View File

@@ -39,6 +39,10 @@ public:
const std::vector<std::string> &getAppliedClasses() const;
void toggleClass(const std::string &name);
// Updates the styling information to reflect the current array
// of applied classes.
void updateClasses();
public:
std::shared_ptr<Sprite> sprite;
std::shared_ptr<StyleLayerGroup> layers;
@@ -49,9 +53,6 @@ private:
void updateSources();
void updateSources(const std::shared_ptr<StyleLayerGroup> &group);
void updateProperties(const std::shared_ptr<StyleLayerGroup> &group, float z, timestamp t);
void updateClasses();
private:
std::set<std::shared_ptr<Source>> activeSources;

View File

@@ -24,8 +24,8 @@ public:
std::unique_ptr<const RasterizeProperties> &&rasterize);
template <typename T> const T &getProperties() {
if (style.is<T>()) {
return style.get<T>();
if (properties.is<T>()) {
return properties.get<T>();
} else {
return defaultStyleProperties<T>();
}
@@ -36,7 +36,7 @@ public:
// Updates the StyleProperties information in this layer by evaluating all
// pending transitions and applied classes in order.
void updateStyle(float z);
void updateProperties(float z, timestamp t);
// Sets the list of classes and creates transitions to the currently applied values.
void setClasses(const std::vector<std::string> &class_names, timestamp now,
@@ -47,6 +47,11 @@ private:
void applyClassProperties(ClassID class_id, std::set<PropertyKey> &already_applied,
timestamp now, const PropertyTransition &defaultTransition);
// Sets the properties of this object by evaluating all pending transitions and
// aplied classes in order.
template <typename T> void applyStyleProperties(float z, timestamp t);
template <typename T> void applyStyleProperty(PropertyKey key, T &, float z, timestamp t);
public:
// The name of this layer.
const std::string id;
@@ -66,7 +71,7 @@ private:
public:
// Stores the evaluated, and cascaded styling information, specific to this
// layer's type.
StyleProperties style;
StyleProperties properties;
// Rasterization properties are used for prerendering the tile to a bitmap,
// which is then used as a raster image instead of rendering this layer

View File

@@ -11,6 +11,7 @@ class StyleLayerGroup {
public:
void setClasses(const std::vector<std::string> &class_names, timestamp now,
const PropertyTransition &defaultTransition);
void updateProperties(float z, timestamp t);
public:
std::vector<std::shared_ptr<StyleLayer>> layers;

View File

@@ -59,6 +59,11 @@ private:
template <typename T>
bool parseStyleProperty(const char *property_name, const std::vector<PropertyKey> &keys, ClassProperties &klass, JSVal value);
template <typename T>
bool parseFunction(PropertyKey key, ClassProperties &klass, JSVal value);
template <typename T>
T parseFunctionArgument(JSVal value);
FilterExpression parseFilter(JSVal, FilterExpression::Operator op);
FilterExpression parseFilter(JSVal);

View File

@@ -1,86 +1,131 @@
#include <llmr/style/function_properties.hpp>
#include <llmr/style/types.hpp>
#include <cmath>
namespace llmr {
float functions::null(float, const std::vector<float>&) {
return 0;
template <>
bool LinearFunction<bool>::evaluate(float z) const {
return z < z_base ? slope >= 0 : z > z_base ? slope >= 0 : value;
}
float functions::constant(float, const std::vector<float>& values) {
return values.size() >= 1 ? values.front() : 0.0f;
template <>
float LinearFunction<float>::evaluate(float z) const {
return std::fmin(std::fmax(min, value + (z - z_base) * slope), max);
}
float functions::min(float z, const std::vector<float>& values) {
return values.size() >= 1 && z >= values.front();
template <>
Color LinearFunction<Color>::evaluate(float z) const {
return {{
std::fmin(std::fmax(min[0], value[0] + (z - z_base) * slope), max[0]),
std::fmin(std::fmax(min[1], value[1] + (z - z_base) * slope), max[1]),
std::fmin(std::fmax(min[2], value[2] + (z - z_base) * slope), max[2]),
std::fmin(std::fmax(min[3], value[3] + (z - z_base) * slope), max[3])
}};
}
float functions::max(float z, const std::vector<float>& values) {
return values.size() >= 1 && z <= values.front();
template <>
bool ExponentialFunction<bool>::evaluate(float z) const {
return z < z_base ? slope >= 0 : z > z_base ? slope >= 0 : value;
}
float functions::stops(float z, const std::vector<float>& stops) {
// We need an even number of stop values.
if (stops.size() % 2 != 0) return 0;
template <>
float ExponentialFunction<float>::evaluate(float z) const {
return std::fmin(std::fmax(min, value + std::pow(1.75, (z - z_base)) * slope), max);
}
template <>
Color ExponentialFunction<Color>::evaluate(float z) const {
return {{
std::fmin(std::fmax(min[0], value[0] + float(std::pow(1.75, (z - z_base))) * slope), max[0]),
std::fmin(std::fmax(min[1], value[1] + float(std::pow(1.75, (z - z_base))) * slope), max[1]),
std::fmin(std::fmax(min[2], value[2] + float(std::pow(1.75, (z - z_base))) * slope), max[2]),
std::fmin(std::fmax(min[3], value[3] + float(std::pow(1.75, (z - z_base))) * slope), max[3])
}};
}
template <typename T>
inline T exponentialInterpolate(T smaller, T larger, const float factor);
template <>
inline float exponentialInterpolate(const float smaller, const float larger, const float factor) {
// Linear interpolation if base is 0
if (smaller == 0.0f) {
return factor * larger;
}
// Exponential interpolation between the values
return smaller * std::pow(larger / smaller, factor);
}
template <>
inline bool exponentialInterpolate(const bool smaller, const bool larger, const float factor) {
return exponentialInterpolate(float(smaller), float(larger), factor);
}
template <>
inline Color exponentialInterpolate(const Color smaller, const Color larger, const float factor) {
return {{
exponentialInterpolate(smaller[0], larger[0], factor),
exponentialInterpolate(smaller[1], larger[1], factor),
exponentialInterpolate(smaller[2], larger[2], factor),
exponentialInterpolate(smaller[3], larger[3], factor)
}};
}
template <typename T>
T exponentialDefault();
template <> bool exponentialDefault() { return true; }
template <> float exponentialDefault() { return 1.0f; }
template <> Color exponentialDefault() { return {{ 0, 0, 0, 1 }}; }
template <typename T>
T StopsFunction<T>::evaluate(float z) const {
bool smaller = false;
float smaller_z = 0.0;
float smaller_val = 0.0;
float smaller_z = 0.0f;
T smaller_val {};
bool larger = false;
float larger_z = 0.0;
float larger_val = 0.0;
float larger_z = 0.0f;
T larger_val {};
for (uint32_t i = 0; i < stops.size(); i += 2) {
float stop_z = stops[i];
float stop_val = stops[i + 1];
if (stop_z <= z && (!smaller || smaller_z < stop_z)) { smaller = true; smaller_z = stop_z; smaller_val = stop_val; }
if (stop_z >= z && (!larger || larger_z > stop_z)) { larger = true; larger_z = stop_z; larger_val = stop_val; }
for (uint32_t i = 0; i < values.size(); i++) {
float stop_z = values[i].first;
T stop_val = values[i].second;
if (stop_z <= z && (!smaller || smaller_z < stop_z)) {
smaller = true;
smaller_z = stop_z;
smaller_val = stop_val;
}
if (stop_z >= z && (!larger || larger_z > stop_z)) {
larger = true;
larger_z = stop_z;
larger_val = stop_val;
}
}
if (smaller && larger) {
if (larger_z == smaller_z || larger_val == smaller_val) return smaller_val;
if (larger_z == smaller_z || larger_val == smaller_val) {
return smaller_val;
}
float factor = (z - smaller_z) / (larger_z - smaller_z);
// Linear interpolation if base is 0
if (smaller_val == 0) return factor * larger_val;
// Exponential interpolation between the values
return smaller_val * std::pow(larger_val / smaller_val, factor);
return exponentialInterpolate<T>(smaller_val, larger_val, factor);
} else if (larger) {
return larger_val;
} else if (smaller) {
return smaller_val;
} else {
// No stop defined.
return 1;
return exponentialDefault<T>();
}
}
float functions::linear(float z, const std::vector<float>& values) {
if (values.size() != 5) {
return 0;
}
const float z_base = values[0];
const float val = values[1];
const float slope = values[2];
const float min = values[3];
const float max = values[4];
return std::fmin(std::fmax(min, val + (z - z_base) * slope), max);
}
float functions::exponential(float z, const std::vector<float>& values) {
if (values.size() != 5) {
return 0;
}
const float z_base = values[0];
const float val = values[1];
const float slope = values[2];
const float min = values[3];
const float max = values[4];
return std::fmin(std::fmax(min, val + std::pow(1.75, (z - z_base)) * slope), max);
}
template bool StopsFunction<bool>::evaluate(float z) const;
template float StopsFunction<float>::evaluate(float z) const;
template Color StopsFunction<Color>::evaluate(float z) const;
}

View File

@@ -51,6 +51,6 @@ const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
{ PropertyKey::BackgroundColor, Color({{ 0, 0, 0, 0 }}) },
};
const PropertyValue PropertyFallbackValue::defaultProperty;
const PropertyValue PropertyFallbackValue::defaultProperty = PropertyValue();
}

View File

@@ -38,154 +38,14 @@ void Style::updateSources(const std::shared_ptr<StyleLayerGroup> &group) {
}
}
StyleProperties resetClassProperties(StyleLayer &layer) {
if (layer.layers) {
return CompositeProperties();
} else if (layer.bucket) {
switch (layer.bucket->type) {
case BucketType::Fill: return FillProperties(); break;
case BucketType::Line: return LineProperties(); break;
case BucketType::Icon: return IconProperties(); break;
case BucketType::Text: return TextProperties(); break;
case BucketType::Raster: return RasterProperties(); break;
default: return std::false_type(); break;
}
} else {
return BackgroundProperties();
}
}
template <typename T>
void applyProperty(const ClassProperties &properties, PropertyKey key, T &target) {
auto it = properties.properties.find(key);
if (it != properties.properties.end()) {
const PropertyValue &value = it->second;
if (value.is<T>()) {
target = value.get<T>();
}
}
}
template <typename T>
void applyProperty(const ClassProperties &properties, PropertyKey key, T &target, float z) {
auto it = properties.properties.find(key);
if (it != properties.properties.end()) {
const PropertyValue &value = it->second;
if (value.is<FunctionProperty>()) {
target = value.get<FunctionProperty>().evaluate<T>(z);
} else if (value.is<bool>()) {
target = value.get<bool>();
}
}
}
void applyClassProperties(StyleProperties &style, const ClassProperties &properties, float z) {
if (style.is<FillProperties>()) {
FillProperties &fill = style.get<FillProperties>();
applyProperty(properties, PropertyKey::FillEnabled, fill.enabled, z);
applyProperty(properties, PropertyKey::FillAntialias, fill.antialias, z);
applyProperty(properties, PropertyKey::FillOpacity, fill.opacity, z);
applyProperty(properties, PropertyKey::FillColor, fill.fill_color);
applyProperty(properties, PropertyKey::FillOutlineColor, fill.stroke_color);
applyProperty(properties, PropertyKey::FillTranslateX, fill.translate[0], z);
applyProperty(properties, PropertyKey::FillTranslateY, fill.translate[1], z);
applyProperty(properties, PropertyKey::FillTranslateAnchor, fill.translateAnchor);
applyProperty(properties, PropertyKey::FillImage, fill.image);
} else if (style.is<LineProperties>()) {
LineProperties &line = style.get<LineProperties>();
applyProperty(properties, PropertyKey::LineEnabled, line.enabled, z);
applyProperty(properties, PropertyKey::LineOpacity, line.opacity, z);
applyProperty(properties, PropertyKey::LineColor, line.color);
applyProperty(properties, PropertyKey::LineTranslateX, line.translate[0], z);
applyProperty(properties, PropertyKey::LineTranslateY, line.translate[1], z);
applyProperty(properties, PropertyKey::LineTranslateAnchor, line.translateAnchor);
applyProperty(properties, PropertyKey::LineWidth, line.width, z);
applyProperty(properties, PropertyKey::LineOffset, line.offset, z);
applyProperty(properties, PropertyKey::LineBlur, line.blur, z);
applyProperty(properties, PropertyKey::LineDashLand, line.dash_array[0], z);
applyProperty(properties, PropertyKey::LineDashGap, line.dash_array[1], z);
applyProperty(properties, PropertyKey::LineImage, line.image);
} else if (style.is<IconProperties>()) {
IconProperties &icon = style.get<IconProperties>();
applyProperty(properties, PropertyKey::IconEnabled, icon.enabled, z);
applyProperty(properties, PropertyKey::IconOpacity, icon.opacity, z);
applyProperty(properties, PropertyKey::IconRotate, icon.rotate, z);
applyProperty(properties, PropertyKey::IconRotateAnchor, icon.rotate_anchor);
} else if (style.is<TextProperties>()) {
TextProperties &text = style.get<TextProperties>();
applyProperty(properties, PropertyKey::TextEnabled, text.enabled, z);
applyProperty(properties, PropertyKey::TextOpacity, text.opacity, z);
applyProperty(properties, PropertyKey::TextSize, text.size, z);
applyProperty(properties, PropertyKey::TextColor, text.color);
applyProperty(properties, PropertyKey::TextHaloColor, text.halo_color);
applyProperty(properties, PropertyKey::TextHaloWidth, text.halo_width, z);
applyProperty(properties, PropertyKey::TextHaloBlur, text.halo_blur, z);
} else if (style.is<CompositeProperties>()) {
CompositeProperties &composite = style.get<CompositeProperties>();
applyProperty(properties, PropertyKey::CompositeEnabled, composite.enabled, z);
applyProperty(properties, PropertyKey::CompositeOpacity, composite.opacity, z);
} else if (style.is<RasterProperties>()) {
RasterProperties &raster = style.get<RasterProperties>();
applyProperty(properties, PropertyKey::RasterEnabled, raster.enabled, z);
applyProperty(properties, PropertyKey::RasterOpacity, raster.opacity, z);
applyProperty(properties, PropertyKey::RasterSpin, raster.spin, z);
applyProperty(properties, PropertyKey::RasterBrightnessLow, raster.brightness[0], z);
applyProperty(properties, PropertyKey::RasterBrightnessHigh, raster.brightness[1], z);
applyProperty(properties, PropertyKey::RasterSaturation, raster.saturation, z);
applyProperty(properties, PropertyKey::RasterContrast, raster.contrast, z);
applyProperty(properties, PropertyKey::RasterFade, raster.fade, z);
} else if (style.is<BackgroundProperties>()) {
BackgroundProperties &background = style.get<BackgroundProperties>();
applyProperty(properties, PropertyKey::BackgroundColor, background.color);
}
}
void Style::updateProperties(const std::shared_ptr<StyleLayerGroup> &group, float z, timestamp t) {
if (!group) {
return;
}
for (const std::shared_ptr<StyleLayer> &layer : group->layers) {
if (!layer) continue;
if (layer->layers) {
updateProperties(layer->layers, z, t);
}
// Accomodate for different tile size.
if (layer->bucket) {
const StyleBucket &bucket = *layer->bucket;
if (bucket.source) {
const Source &source = *bucket.source;
z += std::log(source.tile_size / 256.0f) / M_LN2;
}
}
layer->style = resetClassProperties(*layer);
// Apply default class (if exists)
auto default_it = layer->styles.find(ClassID::Default);
if (default_it != layer->styles.end()) {
applyClassProperties(layer->style, default_it->second, z);
}
// Apply overriding classes in order.
for (const std::string &class_name : appliedClasses) {
const ClassID class_id = ClassDictionary::Lookup(class_name);
auto class_it = layer->styles.find(class_id);
if (class_it != layer->styles.end()) {
applyClassProperties(layer->style, class_it->second, z);
}
}
}
}
void Style::updateProperties(float z, timestamp t) {
uv::writelock lock(mtx);
updateSources();
updateProperties(layers, z, t);
if (layers) {
layers->updateProperties(z, t);
}
// Apply transitions after the first time.
if (!initial_render_complete) {
@@ -231,6 +91,8 @@ void Style::loadJSON(const uint8_t *const data) {
parser.parse(const_cast<const rapidjson::Document &>(doc));
layers = parser.getLayers();
updateClasses();
}
}

View File

@@ -1,4 +1,6 @@
#include <llmr/style/style_layer.hpp>
#include <llmr/style/style_bucket.hpp>
#include <llmr/map/source.hpp>
#include <llmr/style/style_layer_group.hpp>
#include <llmr/style/property_fallback.hpp>
@@ -94,4 +96,177 @@ void StyleLayer::applyClassProperties(const ClassID class_id,
}
}
//void applyStyleProperties(StyleProperties &style, const ClassProperties &properties, float z) {
// if (style.is<FillProperties>()) {
// FillProperties &fill = style.get<FillProperties>();
// applyStyleProperty(properties, PropertyKey::FillEnabled, fill.enabled, z);
// applyStyleProperty(properties, PropertyKey::FillAntialias, fill.antialias, z);
// applyStyleProperty(properties, PropertyKey::FillOpacity, fill.opacity, z);
// applyStyleProperty(properties, PropertyKey::FillColor, fill.fill_color);
// applyStyleProperty(properties, PropertyKey::FillOutlineColor, fill.stroke_color);
// applyStyleProperty(properties, PropertyKey::FillTranslateX, fill.translate[0], z);
// applyStyleProperty(properties, PropertyKey::FillTranslateY, fill.translate[1], z);
// applyStyleProperty(properties, PropertyKey::FillTranslateAnchor, fill.translateAnchor);
// applyStyleProperty(properties, PropertyKey::FillImage, fill.image);
// } else if (style.is<LineProperties>()) {
// LineProperties &line = style.get<LineProperties>();
// applyStyleProperty(properties, PropertyKey::LineEnabled, line.enabled, z);
// applyStyleProperty(properties, PropertyKey::LineOpacity, line.opacity, z);
// applyStyleProperty(properties, PropertyKey::LineColor, line.color);
// applyStyleProperty(properties, PropertyKey::LineTranslateX, line.translate[0], z);
// applyStyleProperty(properties, PropertyKey::LineTranslateY, line.translate[1], z);
// applyStyleProperty(properties, PropertyKey::LineTranslateAnchor, line.translateAnchor);
// applyStyleProperty(properties, PropertyKey::LineWidth, line.width, z);
// applyStyleProperty(properties, PropertyKey::LineOffset, line.offset, z);
// applyStyleProperty(properties, PropertyKey::LineBlur, line.blur, z);
// applyStyleProperty(properties, PropertyKey::LineDashLand, line.dash_array[0], z);
// applyStyleProperty(properties, PropertyKey::LineDashGap, line.dash_array[1], z);
// applyStyleProperty(properties, PropertyKey::LineImage, line.image);
// } else if (style.is<IconProperties>()) {
// IconProperties &icon = style.get<IconProperties>();
// applyStyleProperty(properties, PropertyKey::IconEnabled, icon.enabled, z);
// applyStyleProperty(properties, PropertyKey::IconOpacity, icon.opacity, z);
// applyStyleProperty(properties, PropertyKey::IconRotate, icon.rotate, z);
// applyStyleProperty(properties, PropertyKey::IconRotateAnchor, icon.rotate_anchor);
// } else if (style.is<TextProperties>()) {
// TextProperties &text = style.get<TextProperties>();
// applyStyleProperty(properties, PropertyKey::TextEnabled, text.enabled, z);
// applyStyleProperty(properties, PropertyKey::TextOpacity, text.opacity, z);
// applyStyleProperty(properties, PropertyKey::TextSize, text.size, z);
// applyStyleProperty(properties, PropertyKey::TextColor, text.color);
// applyStyleProperty(properties, PropertyKey::TextHaloColor, text.halo_color);
// applyStyleProperty(properties, PropertyKey::TextHaloWidth, text.halo_width, z);
// applyStyleProperty(properties, PropertyKey::TextHaloBlur, text.halo_blur, z);
// } else if (style.is<CompositeProperties>()) {
// CompositeProperties &composite = style.get<CompositeProperties>();
// applyStyleProperty(properties, PropertyKey::CompositeEnabled, composite.enabled, z);
// applyStyleProperty(properties, PropertyKey::CompositeOpacity, composite.opacity, z);
// } else if (style.is<RasterProperties>()) {
// RasterProperties &raster = style.get<RasterProperties>();
// applyStyleProperty(properties, PropertyKey::RasterEnabled, raster.enabled, z);
// applyStyleProperty(properties, PropertyKey::RasterOpacity, raster.opacity, z);
// applyStyleProperty(properties, PropertyKey::RasterSpin, raster.spin, z);
// applyStyleProperty(properties, PropertyKey::RasterBrightnessLow, raster.brightness[0], z);
// applyStyleProperty(properties, PropertyKey::RasterBrightnessHigh, raster.brightness[1], z);
// applyStyleProperty(properties, PropertyKey::RasterSaturation, raster.saturation, z);
// applyStyleProperty(properties, PropertyKey::RasterContrast, raster.contrast, z);
// applyStyleProperty(properties, PropertyKey::RasterFade, raster.fade, z);
// } else if (style.is<BackgroundProperties>()) {
// BackgroundProperties &background = style.get<BackgroundProperties>();
// applyStyleProperty(properties, PropertyKey::BackgroundColor, background.color);
// }
//}
//
template <typename T>
struct PropertyEvaluator {
typedef T result_type;
PropertyEvaluator(float z) : z(z) {}
template <typename P, typename std::enable_if<std::is_convertible<P, T>::value, int>::type = 0>
T operator()(const P &value) const {
return value;
}
T operator()(const Function<T> &value) const {
return util::apply_visitor(FunctionEvaluator<T>(z), value);
}
template <typename P, typename std::enable_if<!std::is_convertible<P, T>::value, int>::type = 0>
T operator()(const P &) const {
return T();
}
private:
const float z;
};
template <typename T>
void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, const timestamp t) {
auto it = appliedStyle.find(key);
if (it != appliedStyle.end()) {
AppliedClassProperties &applied = it->second;
// Iterate through all properties that we need to apply in order.
const PropertyEvaluator<T> evaluator(z);
for (AppliedClassProperty &property : applied.properties) {
if (property.end >= t) {
// We overwrite the current property with the new value.
target = util::apply_visitor(evaluator, property.value);
} else if (property.begin <= t) {
// We overwrite the current property partially with the new value.
float progress = float(t - property.begin) / float(property.end - property.begin);
target = util::apply_visitor(evaluator, property.value);
// target = (1.0f - progress) * target + (progress) *
// evaluateProperty(property.value, target, z);
}
}
}
}
template <typename T>
void StyleLayer::applyStyleProperties(float, timestamp) {
properties.set<std::false_type>();
}
template <>
void StyleLayer::applyStyleProperties<FillProperties>(const float z, const timestamp t) {
properties.set<FillProperties>();
FillProperties &fill = properties.get<FillProperties>();
applyStyleProperty(PropertyKey::FillEnabled, fill.enabled, z, t);
applyStyleProperty(PropertyKey::FillAntialias, fill.antialias, z, t);
applyStyleProperty(PropertyKey::FillOpacity, fill.opacity, z, t);
applyStyleProperty(PropertyKey::FillColor, fill.fill_color, z, t);
applyStyleProperty(PropertyKey::FillOutlineColor, fill.stroke_color, z, t);
applyStyleProperty(PropertyKey::FillTranslateX, fill.translate[0], z, t);
applyStyleProperty(PropertyKey::FillTranslateY, fill.translate[1], z, t);
applyStyleProperty(PropertyKey::FillTranslateAnchor, fill.translateAnchor, z, t);
applyStyleProperty(PropertyKey::FillImage, fill.image, z, t);
}
template <>
void StyleLayer::applyStyleProperties<LineProperties>(const float z, const timestamp t) {
properties.set<LineProperties>();
LineProperties &line = properties.get<LineProperties>();
applyStyleProperty(PropertyKey::LineEnabled, line.enabled, z, t);
applyStyleProperty(PropertyKey::LineOpacity, line.opacity, z, t);
applyStyleProperty(PropertyKey::LineColor, line.color, z, t);
applyStyleProperty(PropertyKey::LineTranslateX, line.translate[0], z, t);
applyStyleProperty(PropertyKey::LineTranslateY, line.translate[1], z, t);
applyStyleProperty(PropertyKey::LineTranslateAnchor, line.translateAnchor, z, t);
applyStyleProperty(PropertyKey::LineWidth, line.width, z, t);
applyStyleProperty(PropertyKey::LineOffset, line.offset, z, t);
applyStyleProperty(PropertyKey::LineBlur, line.blur, z, t);
applyStyleProperty(PropertyKey::LineDashLand, line.dash_array[0], z, t);
applyStyleProperty(PropertyKey::LineDashGap, line.dash_array[1], z, t);
applyStyleProperty(PropertyKey::LineImage, line.image, z, t);
}
void StyleLayer::updateProperties(float z, const timestamp t) {
if (layers) {
layers->updateProperties(z, t);
}
// Accomodate for different tile size.
if (bucket && bucket->source) {
z += std::log(bucket->source->tile_size / 256.0f) / M_LN2;
}
if (layers) {
applyStyleProperties<CompositeProperties>(z, t);
} else if (bucket) {
switch (bucket->type) {
case BucketType::Fill: applyStyleProperties<FillProperties>(z, t); break;
case BucketType::Line: applyStyleProperties<LineProperties>(z, t); break;
case BucketType::Icon: applyStyleProperties<IconProperties>(z, t); break;
case BucketType::Text: applyStyleProperties<TextProperties>(z, t); break;
case BucketType::Raster: applyStyleProperties<RasterProperties>(z, t); break;
default: properties.set<std::false_type>(); break;
}
} else {
return applyStyleProperties<BackgroundProperties>(z, t);
}
}
}

View File

@@ -11,4 +11,12 @@ void StyleLayerGroup::setClasses(const std::vector<std::string> &class_names, ti
}
}
void StyleLayerGroup::updateProperties(float z, timestamp t) {
for (const std::shared_ptr<StyleLayer> &layer: layers) {
if (layer) {
layer->updateProperties(z, t);
}
}
}
}

View File

@@ -172,21 +172,150 @@ void StyleParser::parseSources(JSVal value) {
#pragma mark - Parse Style Properties
template<> bool StyleParser::parseStyleProperty<bool>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
if (!value.HasMember(property_name)) {
return false;
Color parseColor(JSVal value) {
if (!value.IsString()) {
fprintf(stderr, "[WARNING] color value must be a string\n");
return Color{{ 0, 0, 0, 0 }};
}
JSVal rvalue = replaceConstant(value[property_name]);
if (!rvalue.IsBool()) {
fprintf(stderr, "[WARNING] value of '%s' must be a boolean\n", property_name);
return false;
}
CSSColorParser::Color css_color = CSSColorParser::parse({ value.GetString(), value.GetStringLength() });
klass.set(key, bool(value.GetBool()));
return true;
// 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 <>
bool StyleParser::parseFunctionArgument(JSVal value) {
JSVal rvalue = replaceConstant(value);
if (rvalue.IsBool()) {
return rvalue.GetBool();
} else if (rvalue.IsNumber()) {
return rvalue.GetDouble();
} else {
fprintf(stderr, "[WARNING] function argument must be a boolean or numeric value");
return false;
}
}
template <>
float StyleParser::parseFunctionArgument(JSVal value) {
JSVal rvalue = replaceConstant(value);
if (rvalue.IsNumber()) {
return rvalue.GetDouble();
} else {
fprintf(stderr, "[WARNING] function argument must be a numeric value");
return 0.0f;
}
}
template <>
Color StyleParser::parseFunctionArgument(JSVal value) {
JSVal rvalue = replaceConstant(value);
return parseColor(rvalue);
}
template <typename T>
bool StyleParser::parseFunction(PropertyKey key, ClassProperties &klass, JSVal value) {
if (!value.HasMember("fn")) {
fprintf(stderr, "[WARNING] function must specify a function name\n");
return false;
}
JSVal value_fn = value["fn"];
if (!value_fn.IsString()) {
fprintf(stderr, "[WARNING] function must specify a function type\n");
return false;
}
const std::string type { value_fn.GetString(), value_fn.GetStringLength() };
if (type == "linear") {
if (!value.HasMember("z")) {
fprintf(stderr, "[WARNING] linear function must specify a base zoom level\n");
return false;
}
if (!value.HasMember("val")) {
fprintf(stderr, "[WARNING] linear function must specify a base value\n");
return false;
}
const float z_base = parseFunctionArgument<float>(value["z"]);
const T val = parseFunctionArgument<T>(value["val"]);
const float slope = value.HasMember("slope") ? parseFunctionArgument<float>(value["slope"]) : 1;
const T min = value.HasMember("min") ? parseFunctionArgument<T>(value["min"]) : T();
const T max = value.HasMember("max") ? parseFunctionArgument<T>(value["max"]) : T();
klass.set(key, LinearFunction<T>(val, z_base, slope, min, max));
return true;
}
else if (type == "exponential") {
if (!value.HasMember("z")) {
fprintf(stderr, "[WARNING] exponential function must specify a base zoom level\n");
return false;
}
if (!value.HasMember("val")) {
fprintf(stderr, "[WARNING] exponential function must specify a base value\n");
return false;
}
const float z_base = parseFunctionArgument<float>(value["z"]);
const T val = parseFunctionArgument<T>(value["val"]);
const float slope = value.HasMember("slope") ? parseFunctionArgument<float>(value["slope"]) : 1;
const T min = value.HasMember("min") ? parseFunctionArgument<T>(value["min"]) : T();
const T max = value.HasMember("max") ? parseFunctionArgument<T>(value["max"]) : T();
klass.set(key, ExponentialFunction<T>(val, z_base, slope, min, max));
return true;
}
else if (type == "stops") {
if (!value.HasMember("stops")) {
fprintf(stderr, "[WARNING] stops function must specify a stops array\n");
return false;
}
JSVal value_stops = value["stops"];
if (!value_stops.IsArray()) {
fprintf(stderr, "[WARNING] stops function must specify a stops array\n");
return false;
}
std::vector<std::pair<float, T>> stops;
for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
JSVal stop = value[i];
if (stop.IsObject()) {
if (!stop.HasMember("z")) {
fprintf(stderr, "[WARNING] stop must have zoom level specification\n");
return false;
}
if (!stop.HasMember("val")) {
fprintf(stderr, "[WARNING] stop must have value specification\n");
return false;
}
JSVal z = stop["z"];
if (!z.IsNumber()) {
fprintf(stderr, "[WARNING] zoom level in stop must be a number\n");
return false;
}
stops.emplace_back(z.GetDouble(), parseFunctionArgument<T>(stop["val"]));
} else {
fprintf(stderr, "[WARNING] function argument must be a numeric value\n");
return false;
}
}
klass.set(key, StopsFunction<T>(stops));
return true;
}
else {
fprintf(stderr, "[WARNING] function type '%s' is unknown\n", type.c_str());
}
return false;
}
template<> bool StyleParser::parseStyleProperty<std::string>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
if (!value.HasMember(property_name)) {
@@ -218,7 +347,6 @@ template<> bool StyleParser::parseStyleProperty<TranslateAnchorType>(const char
return true;
}
template<> bool StyleParser::parseStyleProperty<RotateAnchorType>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
if (!value.HasMember(property_name)) {
return false;
@@ -234,29 +362,6 @@ template<> bool StyleParser::parseStyleProperty<RotateAnchorType>(const char *pr
return true;
}
template<> bool StyleParser::parseStyleProperty<Color>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
if (!value.HasMember(property_name)) {
return false;
}
JSVal rvalue = replaceConstant(value[property_name]);
if (!rvalue.IsString()) {
fprintf(stderr, "[WARNING] value of '%s' must be a string\n", property_name);
return false;
}
CSSColorParser::Color css_color = CSSColorParser::parse({ rvalue.GetString(), rvalue.GetStringLength() });
// Premultiply the color.
const float factor = css_color.a / 255;
klass.set(key, Color{{(float)css_color.r * factor,
(float)css_color.g * factor,
(float)css_color.b * factor,
css_color.a}});
return true;
}
template <> bool StyleParser::parseStyleProperty<PropertyTransition>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
if (!value.HasMember(property_name)) {
return false;
@@ -281,82 +386,64 @@ template <> bool StyleParser::parseStyleProperty<PropertyTransition>(const char
return true;
}
FunctionProperty::fn parseFunctionType(JSVal type) {
if (type.IsString()) {
std::string t { type.GetString(), type.GetStringLength() };
if (t == "constant") {
return &functions::constant;
} else if (t == "linear") {
return &functions::linear;
} else if (t == "stops") {
return &functions::stops;
} else if (t == "exponential") {
return &functions::exponential;
} else if (t == "min") {
return &functions::min;
} else if (t == "max") {
return &functions::max;
} else {
throw Style::exception("unknown function type");
}
} else {
throw Style::exception("function type must be a string");
}
}
template<> bool StyleParser::parseStyleProperty<FunctionProperty>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
template<> bool StyleParser::parseStyleProperty<Function<bool>>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
if (!value.HasMember(property_name)) {
return false;
}
JSVal rvalue = replaceConstant(value[property_name]);
FunctionProperty property;
if (rvalue.IsArray()) {
if (rvalue.Size() < 1) {
fprintf(stderr, "[WARNING] function of '%s' does not have arguments\n", property_name);
} else {
JSVal rvalue = replaceConstant(value[property_name]);
if (rvalue.IsObject()) {
return parseFunction<bool>(key, klass, rvalue);
} else if (rvalue.IsNumber()) {
klass.set(key, bool(rvalue.GetDouble()));
return true;
} else if (rvalue.IsBool()) {
klass.set(key, bool(rvalue.GetBool()));
return true;
} else {
fprintf(stderr, "[WARNING] value of '%s' must be a boolean, or a boolean function\n", property_name);
return false;
}
property.function = parseFunctionType(rvalue[(rapidjson::SizeType)0]);
for (rapidjson::SizeType i = 1; i < rvalue.Size(); ++i) {
JSVal stop = rvalue[i];
if (stop.IsObject()) {
if (!stop.HasMember("z")) {
throw Style::exception("stop must have zoom level specification");
}
JSVal z = stop["z"];
if (!z.IsNumber()) {
throw Style::exception("zoom level in stops must be a number");
}
property.values.push_back(z.GetDouble());
if (!stop.HasMember("val")) {
throw Style::exception("stop must have value specification");
}
JSVal val = stop["val"];
if (!val.IsNumber()) {
throw Style::exception("value in stops must be a number");
}
property.values.push_back(val.GetDouble());
} else if (stop.IsNumber()) {
property.values.push_back(stop.GetDouble());
} else if (stop.IsBool()) {
property.values.push_back(stop.GetBool());
} else {
throw Style::exception("function argument must be a numeric value");
}
}
} else if (rvalue.IsNumber()) {
property.function = &functions::constant;
property.values.push_back(rvalue.GetDouble());
}
klass.set(key, std::move(property));
return true;
}
template<> bool StyleParser::parseStyleProperty<Function<float>>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
if (!value.HasMember(property_name)) {
return false;
} else {
JSVal rvalue = replaceConstant(value[property_name]);
if (rvalue.IsObject()) {
return parseFunction<float>(key, klass, rvalue);
} else if (rvalue.IsNumber()) {
klass.set(key, float(rvalue.GetDouble()));
return true;
} else if (rvalue.IsBool()) {
klass.set(key, float(rvalue.GetBool()));
return true;
} else {
fprintf(stderr, "[WARNING] value of '%s' must be a number, or a number function\n", property_name);
return false;
}
}
}
template<> bool StyleParser::parseStyleProperty<Function<Color>>(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
if (!value.HasMember(property_name)) {
return false;
} else {
JSVal rvalue = replaceConstant(value[property_name]);
if (rvalue.IsObject()) {
return parseFunction<Color>(key, klass, rvalue);
} else if (rvalue.IsString()) {
klass.set(key, parseColor(rvalue));
return true;
} else {
fprintf(stderr, "[WARNING] value of '%s' must be a color, or a color function\n", property_name);
return false;
}
}
}
template <typename T>
bool StyleParser::parseStyleProperty(const char *property_name, const std::vector<PropertyKey> &keys, ClassProperties &klass, JSVal value) {
if (value.HasMember(property_name)) {
@@ -490,84 +577,84 @@ void StyleParser::parseStyles(JSVal value, std::map<ClassID, ClassProperties> &s
void StyleParser::parseStyle(JSVal value, ClassProperties &klass) {
using Key = PropertyKey;
parseStyleProperty<FunctionProperty>("fill-enabled", Key::FillEnabled, klass, value);
parseStyleProperty<Function<bool>>("fill-enabled", Key::FillEnabled, klass, value);
parseStyleProperty<PropertyTransition>("transition-fill-enabled", Key::FillEnabled, klass, value);
parseStyleProperty<FunctionProperty>("fill-antialias", Key::FillAntialias, klass, value);
parseStyleProperty<FunctionProperty>("fill-opacity", Key::FillOpacity, klass, value);
parseStyleProperty<Function<bool>>("fill-antialias", Key::FillAntialias, klass, value);
parseStyleProperty<Function<float>>("fill-opacity", Key::FillOpacity, klass, value);
parseStyleProperty<PropertyTransition>("transition-fill-opacity", Key::FillOpacity, klass, value);
parseStyleProperty<Color>("fill-color", Key::FillColor, klass, value);
parseStyleProperty<Function<Color>>("fill-color", Key::FillColor, klass, value);
parseStyleProperty<PropertyTransition>("transition-fill-color", Key::FillColor, klass, value);
parseStyleProperty<Color>("fill-outline-color", Key::FillOutlineColor, klass, value);
parseStyleProperty<Function<Color>>("fill-outline-color", Key::FillOutlineColor, klass, value);
parseStyleProperty<PropertyTransition>("transition-fill-outline-color", Key::FillOutlineColor, klass, value);
parseStyleProperty<FunctionProperty>("fill-translate", { Key::FillTranslateX, Key::FillTranslateY }, klass, value);
parseStyleProperty<Function<float>>("fill-translate", { Key::FillTranslateX, Key::FillTranslateY }, klass, value);
parseStyleProperty<PropertyTransition>("transition-fill-translate", Key::FillTranslate, klass, value);
parseStyleProperty<TranslateAnchorType>("fill-translate-anchor", Key::FillTranslateAnchor, klass, value);
parseStyleProperty<std::string>("fill-image", Key::FillImage, klass, value);
parseStyleProperty<FunctionProperty>("line-enabled", Key::LineEnabled, klass, value);
parseStyleProperty<Function<bool>>("line-enabled", Key::LineEnabled, klass, value);
parseStyleProperty<PropertyTransition>("transition-line-enabled", Key::LineEnabled, klass, value);
parseStyleProperty<FunctionProperty>("line-opacity", Key::LineOpacity, klass, value);
parseStyleProperty<Function<float>>("line-opacity", Key::LineOpacity, klass, value);
parseStyleProperty<PropertyTransition>("transition-line-opacity", Key::LineOpacity, klass, value);
parseStyleProperty<Color>("line-color", Key::LineColor, klass, value);
parseStyleProperty<Function<Color>>("line-color", Key::LineColor, klass, value);
parseStyleProperty<PropertyTransition>("transition-line-color", Key::LineColor, klass, value);
parseStyleProperty<FunctionProperty>("line-translate", { Key::LineTranslateX, Key::LineTranslateY }, klass, value);
parseStyleProperty<Function<float>>("line-translate", { Key::LineTranslateX, Key::LineTranslateY }, klass, value);
parseStyleProperty<PropertyTransition>("transition-line-translate", Key::LineTranslate, klass, value);
parseStyleProperty<TranslateAnchorType>("line-translate-anchor", Key::LineTranslateAnchor, klass, value);
parseStyleProperty<FunctionProperty>("line-width", Key::LineWidth, klass, value);
parseStyleProperty<Function<float>>("line-width", Key::LineWidth, klass, value);
parseStyleProperty<PropertyTransition>("transition-line-width", Key::LineWidth, klass, value);
parseStyleProperty<FunctionProperty>("line-offset", Key::LineOffset, klass, value);
parseStyleProperty<Function<float>>("line-offset", Key::LineOffset, klass, value);
parseStyleProperty<PropertyTransition>("transition-line-offset", Key::LineOffset, klass, value);
parseStyleProperty<FunctionProperty>("line-blur", Key::LineBlur, klass, value);
parseStyleProperty<Function<float>>("line-blur", Key::LineBlur, klass, value);
parseStyleProperty<PropertyTransition>("transition-line-blur", Key::LineBlur, klass, value);
parseStyleProperty<FunctionProperty>("line-dasharray", { Key::LineDashLand, Key::LineDashGap }, klass, value);
parseStyleProperty<Function<float>>("line-dasharray", { Key::LineDashLand, Key::LineDashGap }, klass, value);
parseStyleProperty<PropertyTransition>("transition-line-dasharray", Key::LineDashArray, klass, value);
parseStyleProperty<std::string>("line-image", Key::LineImage, klass, value);
parseStyleProperty<FunctionProperty>("icon-enabled", Key::IconEnabled, klass, value);
parseStyleProperty<Function<bool>>("icon-enabled", Key::IconEnabled, klass, value);
parseStyleProperty<PropertyTransition>("transition-icon-enabled", Key::IconEnabled, klass, value);
parseStyleProperty<FunctionProperty>("icon-opacity", Key::IconOpacity, klass, value);
parseStyleProperty<Function<float>>("icon-opacity", Key::IconOpacity, klass, value);
parseStyleProperty<PropertyTransition>("transition-icon-opacity", Key::IconOpacity, klass, value);
parseStyleProperty<FunctionProperty>("icon-rotate", Key::IconRotate, klass, value);
parseStyleProperty<Function<float>>("icon-rotate", Key::IconRotate, klass, value);
parseStyleProperty<RotateAnchorType>("icon-rotate-anchor", Key::IconRotateAnchor, klass, value);
parseStyleProperty<FunctionProperty>("text-enabled", Key::TextEnabled, klass, value);
parseStyleProperty<Function<bool>>("text-enabled", Key::TextEnabled, klass, value);
parseStyleProperty<PropertyTransition>("transition-text-enabled", Key::TextEnabled, klass, value);
parseStyleProperty<FunctionProperty>("text-opacity", Key::TextOpacity, klass, value);
parseStyleProperty<Function<float>>("text-opacity", Key::TextOpacity, klass, value);
parseStyleProperty<PropertyTransition>("transition-text-opacity", Key::TextOpacity, klass, value);
parseStyleProperty<FunctionProperty>("text-size", Key::TextSize, klass, value);
parseStyleProperty<Function<float>>("text-size", Key::TextSize, klass, value);
parseStyleProperty<PropertyTransition>("transition-text-size", Key::TextSize, klass, value);
parseStyleProperty<Color>("text-color", Key::TextColor, klass, value);
parseStyleProperty<Function<Color>>("text-color", Key::TextColor, klass, value);
parseStyleProperty<PropertyTransition>("transition-text-color", Key::TextColor, klass, value);
parseStyleProperty<Color>("text-halo-color", Key::TextHaloColor, klass, value);
parseStyleProperty<Function<Color>>("text-halo-color", Key::TextHaloColor, klass, value);
parseStyleProperty<PropertyTransition>("transition-text-halo-color", Key::TextHaloColor, klass, value);
parseStyleProperty<FunctionProperty>("text-halo-width", Key::TextHaloWidth, klass, value);
parseStyleProperty<Function<float>>("text-halo-width", Key::TextHaloWidth, klass, value);
parseStyleProperty<PropertyTransition>("transition-text-halo-width", Key::TextHaloWidth, klass, value);
parseStyleProperty<FunctionProperty>("text-halo-blur", Key::TextHaloBlur, klass, value);
parseStyleProperty<Function<float>>("text-halo-blur", Key::TextHaloBlur, klass, value);
parseStyleProperty<PropertyTransition>("transition-text-halo-blur", Key::TextHaloBlur, klass, value);
parseStyleProperty<FunctionProperty>("composite-enabled", Key::CompositeEnabled, klass, value);
parseStyleProperty<Function<bool>>("composite-enabled", Key::CompositeEnabled, klass, value);
parseStyleProperty<PropertyTransition>("transition-composite-enabled", Key::CompositeEnabled, klass, value);
parseStyleProperty<FunctionProperty>("composite-opacity", Key::CompositeOpacity, klass, value);
parseStyleProperty<Function<float>>("composite-opacity", Key::CompositeOpacity, klass, value);
parseStyleProperty<PropertyTransition>("transition-composite-opacity", Key::CompositeOpacity, klass, value);
parseStyleProperty<FunctionProperty>("raster-enabled", Key::RasterEnabled, klass, value);
parseStyleProperty<Function<bool>>("raster-enabled", Key::RasterEnabled, klass, value);
parseStyleProperty<PropertyTransition>("transition-raster-enabled", Key::RasterEnabled, klass, value);
parseStyleProperty<FunctionProperty>("raster-opacity", Key::RasterOpacity, klass, value);
parseStyleProperty<Function<float>>("raster-opacity", Key::RasterOpacity, klass, value);
parseStyleProperty<PropertyTransition>("transition-raster-opacity", Key::RasterOpacity, klass, value);
parseStyleProperty<FunctionProperty>("raster-spin", Key::RasterSpin, klass, value);
parseStyleProperty<Function<float>>("raster-spin", Key::RasterSpin, klass, value);
parseStyleProperty<PropertyTransition>("transition-raster-spin", Key::RasterSpin, klass, value);
parseStyleProperty<FunctionProperty>("raster-brightness-low", Key::RasterBrightnessLow, klass, value);
parseStyleProperty<Function<float>>("raster-brightness-low", Key::RasterBrightnessLow, klass, value);
parseStyleProperty<PropertyTransition>("transition-raster-brightness-low", Key::RasterBrightnessLow, klass, value);
parseStyleProperty<FunctionProperty>("raster-brightness-high", Key::RasterBrightnessHigh, klass, value);
parseStyleProperty<Function<float>>("raster-brightness-high", Key::RasterBrightnessHigh, klass, value);
parseStyleProperty<PropertyTransition>("transition-raster-brightness-high", Key::RasterBrightnessHigh, klass, value);
parseStyleProperty<FunctionProperty>("raster-saturation", Key::RasterSaturation, klass, value);
parseStyleProperty<Function<float>>("raster-saturation", Key::RasterSaturation, klass, value);
parseStyleProperty<PropertyTransition>("transition-raster-saturation", Key::RasterSaturation, klass, value);
parseStyleProperty<FunctionProperty>("raster-contrast", Key::RasterContrast, klass, value);
parseStyleProperty<Function<float>>("raster-contrast", Key::RasterContrast, klass, value);
parseStyleProperty<PropertyTransition>("transition-raster-contrast", Key::RasterContrast, klass, value);
parseStyleProperty<FunctionProperty>("raster-fade", Key::RasterFade, klass, value);
parseStyleProperty<Function<float>>("raster-fade", Key::RasterFade, klass, value);
parseStyleProperty<PropertyTransition>("transition-raster-fade", Key::RasterFade, klass, value);
parseStyleProperty<Color>("background-color", Key::BackgroundColor, klass, value);
parseStyleProperty<Function<Color>>("background-color", Key::BackgroundColor, klass, value);
}
std::unique_ptr<const RasterizeProperties> StyleParser::parseRasterize(JSVal value) {