[core] Extract PaintPropertiesMap

This commit is contained in:
John Firebaugh
2015-10-19 18:30:25 -07:00
parent e257ad284b
commit bb6241844a
20 changed files with 322 additions and 277 deletions

View File

@@ -115,7 +115,6 @@ void AnnotationManager::updateStyle(Style& style) {
std::unique_ptr<SymbolLayer> layer = std::make_unique<SymbolLayer>();
layer->id = PointLayerID;
layer->type = StyleLayerType::Symbol;
layer->styles.emplace(ClassID::Default, ClassProperties());
layer->bucket = std::make_shared<StyleBucket>(layer->type);
layer->bucket->name = layer->id;

View File

@@ -37,7 +37,7 @@ void ShapeAnnotationImpl::updateStyle(Style& style) {
paintProperties.set(PropertyKey::LineOpacity, ConstantFunction<float>(properties.opacity));
paintProperties.set(PropertyKey::LineWidth, ConstantFunction<float>(properties.width));
paintProperties.set(PropertyKey::LineColor, ConstantFunction<Color>(properties.color));
layer->styles.emplace(ClassID::Default, std::move(paintProperties));
layer->paints.paints.emplace(ClassID::Default, std::move(paintProperties));
} else if (shape.properties.is<FillPaintProperties>()) {
layer = createFillLayer();
@@ -47,7 +47,7 @@ void ShapeAnnotationImpl::updateStyle(Style& style) {
paintProperties.set(PropertyKey::FillOpacity, ConstantFunction<float>(properties.opacity));
paintProperties.set(PropertyKey::FillColor, ConstantFunction<Color>(properties.fill_color));
paintProperties.set(PropertyKey::FillOutlineColor, ConstantFunction<Color>(properties.stroke_color));
layer->styles.emplace(ClassID::Default, std::move(paintProperties));
layer->paints.paints.emplace(ClassID::Default, std::move(paintProperties));
} else {
beforeLayerID = shape.properties.get<std::string>();
@@ -68,7 +68,7 @@ void ShapeAnnotationImpl::updateStyle(Style& style) {
return;
}
layer->styles = sourceLayer->styles;
layer->paints.paints = sourceLayer->paints.paints;
layer->bucket->layout = sourceLayer->bucket->layout;
}

View File

@@ -2,11 +2,14 @@
namespace mbgl {
RenderPass BackgroundLayer::applyStyleProperties(const StyleCalculationParameters& parameters) {
applyTransitionedStyleProperty(PropertyKey::BackgroundOpacity, properties.opacity, parameters);
applyTransitionedStyleProperty(PropertyKey::BackgroundColor, properties.color, parameters);
applyStyleProperty(PropertyKey::BackgroundImage, properties.image, parameters);
return properties.isVisible() ? RenderPass::Translucent : RenderPass::None;
void BackgroundLayer::recalculate(const StyleCalculationParameters& parameters) {
paints.removeExpiredTransitions(parameters.now);
paints.calculateTransitioned(PropertyKey::BackgroundOpacity, properties.opacity, parameters);
paints.calculateTransitioned(PropertyKey::BackgroundColor, properties.color, parameters);
paints.calculate(PropertyKey::BackgroundImage, properties.image, parameters);
passes = properties.isVisible() ? RenderPass::Translucent : RenderPass::None;
}
}

View File

@@ -8,7 +8,7 @@ namespace mbgl {
class BackgroundLayer : public StyleLayer {
public:
RenderPass applyStyleProperties(const StyleCalculationParameters&) override;
void recalculate(const StyleCalculationParameters&) override;
BackgroundPaintProperties properties;
};

View File

@@ -2,14 +2,17 @@
namespace mbgl {
RenderPass CircleLayer::applyStyleProperties(const StyleCalculationParameters& parameters) {
applyTransitionedStyleProperty(PropertyKey::CircleRadius, properties.radius, parameters);
applyTransitionedStyleProperty(PropertyKey::CircleColor, properties.color, parameters);
applyTransitionedStyleProperty(PropertyKey::CircleOpacity, properties.opacity, parameters);
applyTransitionedStyleProperty(PropertyKey::CircleTranslate, properties.translate, parameters);
applyStyleProperty(PropertyKey::CircleTranslateAnchor, properties.translateAnchor, parameters);
applyTransitionedStyleProperty(PropertyKey::CircleBlur, properties.blur, parameters);
return properties.isVisible() ? RenderPass::Translucent : RenderPass::None;
void CircleLayer::recalculate(const StyleCalculationParameters& parameters) {
paints.removeExpiredTransitions(parameters.now);
paints.calculateTransitioned(PropertyKey::CircleRadius, properties.radius, parameters);
paints.calculateTransitioned(PropertyKey::CircleColor, properties.color, parameters);
paints.calculateTransitioned(PropertyKey::CircleOpacity, properties.opacity, parameters);
paints.calculateTransitioned(PropertyKey::CircleTranslate, properties.translate, parameters);
paints.calculate(PropertyKey::CircleTranslateAnchor, properties.translateAnchor, parameters);
paints.calculateTransitioned(PropertyKey::CircleBlur, properties.blur, parameters);
passes = properties.isVisible() ? RenderPass::Translucent : RenderPass::None;
}
}

View File

@@ -8,7 +8,7 @@ namespace mbgl {
class CircleLayer : public StyleLayer {
public:
RenderPass applyStyleProperties(const StyleCalculationParameters&) override;
void recalculate(const StyleCalculationParameters&) override;
CirclePaintProperties properties;
};

View File

@@ -2,28 +2,28 @@
namespace mbgl {
RenderPass FillLayer::applyStyleProperties(const StyleCalculationParameters& parameters) {
applyStyleProperty(PropertyKey::FillAntialias, properties.antialias, parameters);
applyTransitionedStyleProperty(PropertyKey::FillOpacity, properties.opacity, parameters);
applyTransitionedStyleProperty(PropertyKey::FillColor, properties.fill_color, parameters);
applyTransitionedStyleProperty(PropertyKey::FillOutlineColor, properties.stroke_color, parameters);
applyTransitionedStyleProperty(PropertyKey::FillTranslate, properties.translate, parameters);
applyStyleProperty(PropertyKey::FillTranslateAnchor, properties.translateAnchor, parameters);
applyStyleProperty(PropertyKey::FillImage, properties.image, parameters);
void FillLayer::recalculate(const StyleCalculationParameters& parameters) {
paints.removeExpiredTransitions(parameters.now);
RenderPass result = RenderPass::None;
paints.calculate(PropertyKey::FillAntialias, properties.antialias, parameters);
paints.calculateTransitioned(PropertyKey::FillOpacity, properties.opacity, parameters);
paints.calculateTransitioned(PropertyKey::FillColor, properties.fill_color, parameters);
paints.calculateTransitioned(PropertyKey::FillOutlineColor, properties.stroke_color, parameters);
paints.calculateTransitioned(PropertyKey::FillTranslate, properties.translate, parameters);
paints.calculate(PropertyKey::FillTranslateAnchor, properties.translateAnchor, parameters);
paints.calculate(PropertyKey::FillImage, properties.image, parameters);
passes = RenderPass::None;
if (properties.antialias) {
result |= RenderPass::Translucent;
passes |= RenderPass::Translucent;
}
if (!properties.image.from.empty() || (properties.fill_color[3] * properties.opacity) < 1.0f) {
result |= RenderPass::Translucent;
passes |= RenderPass::Translucent;
} else {
result |= RenderPass::Opaque;
passes |= RenderPass::Opaque;
}
return result;
}
}

View File

@@ -8,7 +8,7 @@ namespace mbgl {
class FillLayer : public StyleLayer {
public:
RenderPass applyStyleProperties(const StyleCalculationParameters&) override;
void recalculate(const StyleCalculationParameters&) override;
FillPaintProperties properties;
};

View File

@@ -2,23 +2,25 @@
namespace mbgl {
RenderPass LineLayer::applyStyleProperties(const StyleCalculationParameters& parameters) {
applyTransitionedStyleProperty(PropertyKey::LineOpacity, properties.opacity, parameters);
applyTransitionedStyleProperty(PropertyKey::LineColor, properties.color, parameters);
applyTransitionedStyleProperty(PropertyKey::LineTranslate, properties.translate, parameters);
applyStyleProperty(PropertyKey::LineTranslateAnchor, properties.translateAnchor, parameters);
applyTransitionedStyleProperty(PropertyKey::LineWidth, properties.width, parameters);
applyTransitionedStyleProperty(PropertyKey::LineGapWidth, properties.gap_width, parameters);
applyTransitionedStyleProperty(PropertyKey::LineBlur, properties.blur, parameters);
applyStyleProperty(PropertyKey::LineDashArray, properties.dash_array, parameters);
applyStyleProperty(PropertyKey::LineImage, properties.image, parameters);
void LineLayer::recalculate(const StyleCalculationParameters& parameters) {
paints.removeExpiredTransitions(parameters.now);
paints.calculateTransitioned(PropertyKey::LineOpacity, properties.opacity, parameters);
paints.calculateTransitioned(PropertyKey::LineColor, properties.color, parameters);
paints.calculateTransitioned(PropertyKey::LineTranslate, properties.translate, parameters);
paints.calculate(PropertyKey::LineTranslateAnchor, properties.translateAnchor, parameters);
paints.calculateTransitioned(PropertyKey::LineWidth, properties.width, parameters);
paints.calculateTransitioned(PropertyKey::LineGapWidth, properties.gap_width, parameters);
paints.calculateTransitioned(PropertyKey::LineBlur, properties.blur, parameters);
paints.calculate(PropertyKey::LineDashArray, properties.dash_array, parameters);
paints.calculate(PropertyKey::LineImage, properties.image, parameters);
// for scaling dasharrays
StyleCalculationParameters dashArrayParams = parameters;
dashArrayParams.z = std::floor(dashArrayParams.z);
applyStyleProperty(PropertyKey::LineWidth, properties.dash_line_width, dashArrayParams);
paints.calculate(PropertyKey::LineWidth, properties.dash_line_width, dashArrayParams);
return properties.isVisible() ? RenderPass::Translucent : RenderPass::None;
passes = properties.isVisible() ? RenderPass::Translucent : RenderPass::None;
}
}

View File

@@ -8,7 +8,7 @@ namespace mbgl {
class LineLayer : public StyleLayer {
public:
RenderPass applyStyleProperties(const StyleCalculationParameters&) override;
void recalculate(const StyleCalculationParameters&) override;
LinePaintProperties properties;
};

View File

@@ -2,15 +2,18 @@
namespace mbgl {
RenderPass RasterLayer::applyStyleProperties(const StyleCalculationParameters& parameters) {
applyTransitionedStyleProperty(PropertyKey::RasterOpacity, properties.opacity, parameters);
applyTransitionedStyleProperty(PropertyKey::RasterHueRotate, properties.hue_rotate, parameters);
applyTransitionedStyleProperty(PropertyKey::RasterBrightnessLow, properties.brightness[0], parameters);
applyTransitionedStyleProperty(PropertyKey::RasterBrightnessHigh, properties.brightness[1], parameters);
applyTransitionedStyleProperty(PropertyKey::RasterSaturation, properties.saturation, parameters);
applyTransitionedStyleProperty(PropertyKey::RasterContrast, properties.contrast, parameters);
applyTransitionedStyleProperty(PropertyKey::RasterFade, properties.fade, parameters);
return properties.isVisible() ? RenderPass::Translucent : RenderPass::None;
void RasterLayer::recalculate(const StyleCalculationParameters& parameters) {
paints.removeExpiredTransitions(parameters.now);
paints.calculateTransitioned(PropertyKey::RasterOpacity, properties.opacity, parameters);
paints.calculateTransitioned(PropertyKey::RasterHueRotate, properties.hue_rotate, parameters);
paints.calculateTransitioned(PropertyKey::RasterBrightnessLow, properties.brightness[0], parameters);
paints.calculateTransitioned(PropertyKey::RasterBrightnessHigh, properties.brightness[1], parameters);
paints.calculateTransitioned(PropertyKey::RasterSaturation, properties.saturation, parameters);
paints.calculateTransitioned(PropertyKey::RasterContrast, properties.contrast, parameters);
paints.calculateTransitioned(PropertyKey::RasterFade, properties.fade, parameters);
passes = properties.isVisible() ? RenderPass::Translucent : RenderPass::None;
}
}

View File

@@ -8,7 +8,7 @@ namespace mbgl {
class RasterLayer : public StyleLayer {
public:
RenderPass applyStyleProperties(const StyleCalculationParameters&) override;
void recalculate(const StyleCalculationParameters&) override;
RasterPaintProperties properties;
};

View File

@@ -4,22 +4,24 @@
namespace mbgl {
RenderPass SymbolLayer::applyStyleProperties(const StyleCalculationParameters& parameters) {
applyTransitionedStyleProperty(PropertyKey::IconOpacity, properties.icon.opacity, parameters);
applyTransitionedStyleProperty(PropertyKey::IconColor, properties.icon.color, parameters);
applyTransitionedStyleProperty(PropertyKey::IconHaloColor, properties.icon.halo_color, parameters);
applyTransitionedStyleProperty(PropertyKey::IconHaloWidth, properties.icon.halo_width, parameters);
applyTransitionedStyleProperty(PropertyKey::IconHaloBlur, properties.icon.halo_blur, parameters);
applyTransitionedStyleProperty(PropertyKey::IconTranslate, properties.icon.translate, parameters);
applyStyleProperty(PropertyKey::IconTranslateAnchor, properties.icon.translate_anchor, parameters);
void SymbolLayer::recalculate(const StyleCalculationParameters& parameters) {
paints.removeExpiredTransitions(parameters.now);
applyTransitionedStyleProperty(PropertyKey::TextOpacity, properties.text.opacity, parameters);
applyTransitionedStyleProperty(PropertyKey::TextColor, properties.text.color, parameters);
applyTransitionedStyleProperty(PropertyKey::TextHaloColor, properties.text.halo_color, parameters);
applyTransitionedStyleProperty(PropertyKey::TextHaloWidth, properties.text.halo_width, parameters);
applyTransitionedStyleProperty(PropertyKey::TextHaloBlur, properties.text.halo_blur, parameters);
applyTransitionedStyleProperty(PropertyKey::TextTranslate, properties.text.translate, parameters);
applyStyleProperty(PropertyKey::TextTranslateAnchor, properties.text.translate_anchor, parameters);
paints.calculateTransitioned(PropertyKey::IconOpacity, properties.icon.opacity, parameters);
paints.calculateTransitioned(PropertyKey::IconColor, properties.icon.color, parameters);
paints.calculateTransitioned(PropertyKey::IconHaloColor, properties.icon.halo_color, parameters);
paints.calculateTransitioned(PropertyKey::IconHaloWidth, properties.icon.halo_width, parameters);
paints.calculateTransitioned(PropertyKey::IconHaloBlur, properties.icon.halo_blur, parameters);
paints.calculateTransitioned(PropertyKey::IconTranslate, properties.icon.translate, parameters);
paints.calculate(PropertyKey::IconTranslateAnchor, properties.icon.translate_anchor, parameters);
paints.calculateTransitioned(PropertyKey::TextOpacity, properties.text.opacity, parameters);
paints.calculateTransitioned(PropertyKey::TextColor, properties.text.color, parameters);
paints.calculateTransitioned(PropertyKey::TextHaloColor, properties.text.halo_color, parameters);
paints.calculateTransitioned(PropertyKey::TextHaloWidth, properties.text.halo_width, parameters);
paints.calculateTransitioned(PropertyKey::TextHaloBlur, properties.text.halo_blur, parameters);
paints.calculateTransitioned(PropertyKey::TextTranslate, properties.text.translate, parameters);
paints.calculate(PropertyKey::TextTranslateAnchor, properties.text.translate_anchor, parameters);
// text-size and icon-size are layout properties but they also need to be evaluated as paint properties:
auto it = bucket->layout.properties.find(PropertyKey::IconSize);
@@ -33,7 +35,7 @@ RenderPass SymbolLayer::applyStyleProperties(const StyleCalculationParameters& p
properties.text.size = mapbox::util::apply_visitor(evaluator, it->second);
}
return properties.isVisible() ? RenderPass::Translucent : RenderPass::None;
passes = properties.isVisible() ? RenderPass::Translucent : RenderPass::None;
}
}

View File

@@ -8,7 +8,7 @@ namespace mbgl {
class SymbolLayer : public StyleLayer {
public:
RenderPass applyStyleProperties(const StyleCalculationParameters&) override;
void recalculate(const StyleCalculationParameters&) override;
SymbolPaintProperties properties;
};

View File

@@ -0,0 +1,114 @@
#include <mbgl/style/paint_properties_map.hpp>
#include <mbgl/style/property_transition.hpp>
#include <mbgl/style/property_fallback.hpp>
#include <mbgl/style/class_properties.hpp>
namespace mbgl {
void PaintPropertiesMap::cascade(const std::vector<std::string>& classes,
const TimePoint& now,
const PropertyTransition& defaultTransition) {
// Stores all keys that we have already added transitions for.
std::set<PropertyKey> alreadyApplied;
// We only apply the default style values if there are no classes set.
if (classes.empty()) {
cascadeClass(ClassID::Default, alreadyApplied, now, defaultTransition);
return;
}
// Reverse iterate through all class names and apply them last to first.
for (auto it = classes.rbegin(); it != classes.rend(); ++it) {
// From here on, we're only dealing with IDs to avoid comparing strings all the time.
cascadeClass(ClassDictionary::Get().lookup(*it), alreadyApplied, now, defaultTransition);
}
// As the last class, apply the default class.
cascadeClass(ClassID::Default, alreadyApplied, now, defaultTransition);
// Make sure that we also transition to the fallback value for keys that aren't changed by
// any applied classes.
for (auto& propertyPair : appliedStyle) {
const PropertyKey key = propertyPair.first;
if (alreadyApplied.find(key) != alreadyApplied.end()) {
// This property has already been set by a previous class, so we don't need to
// transition to the fallback.
continue;
}
AppliedClassPropertyValues &appliedProperties = propertyPair.second;
// Make sure that we don't do double transitions to the fallback value.
if (appliedProperties.mostRecent() == ClassID::Fallback) {
continue;
}
// This property key hasn't been set by a previous class, so we need to add a transition
// to the fallback value for that key.
const TimePoint begin = now + *defaultTransition.delay;
const TimePoint end = begin + *defaultTransition.duration;
const PropertyValue &value = PropertyFallbackValue::Get(key);
appliedProperties.add(ClassID::Fallback, begin, end, value);
}
}
void PaintPropertiesMap::cascadeClass(const ClassID classID,
std::set<PropertyKey>& alreadyApplied,
const TimePoint& now,
const PropertyTransition& defaultTransition) {
auto styleIt = paints.find(classID);
if (styleIt == paints.end()) {
// There is no class in this layer with this class_name.
return;
}
// Loop through all the properties in this style, and add transitions to them, if they're
// not already the most recent transition.
const ClassProperties& classProperties = styleIt->second;
for (const auto& propertyPair : classProperties) {
PropertyKey key = propertyPair.first;
if (alreadyApplied.find(key) != alreadyApplied.end()) {
// This property has already been set by a previous class.
continue;
}
// Mark this property as written by a previous class, so that subsequent
// classes won't override this.
alreadyApplied.insert(key);
// If the most recent transition is not the one with the highest priority, create
// a transition.
AppliedClassPropertyValues &appliedProperties = appliedStyle[key];
if (appliedProperties.mostRecent() != classID) {
PropertyTransition transition = classProperties.getTransition(key);
Duration delay = transition.delay ? *transition.delay : *defaultTransition.delay;
Duration duration = transition.duration ? *transition.duration : *defaultTransition.duration;
const TimePoint begin = now + delay;
const TimePoint end = begin + duration;
const PropertyValue &value = propertyPair.second;
appliedProperties.add(classID, begin, end, value);
}
}
}
bool PaintPropertiesMap::hasTransitions() const {
for (const auto& pair : appliedStyle) {
if (pair.second.hasTransitions()) {
return true;
}
}
return hasPendingTransitions;
}
void PaintPropertiesMap::removeExpiredTransitions(const TimePoint& now) {
for (auto it = appliedStyle.begin(); it != appliedStyle.end();) {
AppliedClassPropertyValues& values = it->second;
values.cleanup(now);
// If the current properties object is empty, remove it from the map entirely.
values.empty() ? appliedStyle.erase(it++) : ++it;
}
// Clear the pending transitions flag upon each update.
hasPendingTransitions = false;
}
}

View File

@@ -0,0 +1,89 @@
#ifndef MBGL_PAINT_PROPERTIES_MAP
#define MBGL_PAINT_PROPERTIES_MAP
#include <mbgl/style/applied_class_properties.hpp>
#include <mbgl/style/property_evaluator.hpp>
#include <mbgl/style/class_dictionary.hpp>
#include <mbgl/style/property_key.hpp>
#include <mbgl/util/interpolate.hpp>
#include <map>
#include <set>
namespace mbgl {
class ClassProperties;
class PropertyTransition;
class PaintPropertiesMap {
public:
void cascade(const std::vector<std::string>& classNames,
const TimePoint& now,
const PropertyTransition& defaultTransition);
bool hasTransitions() const;
void removeExpiredTransitions(const TimePoint& now);
template <typename T>
void calculate(PropertyKey key, T& target, const StyleCalculationParameters& parameters) {
auto it = appliedStyle.find(key);
if (it != appliedStyle.end()) {
AppliedClassPropertyValues &applied = it->second;
// Iterate through all properties that we need to apply in order.
const PropertyEvaluator<T> evaluator(parameters);
for (auto& property : applied.propertyValues) {
if (parameters.now >= property.begin) {
// We overwrite the current property with the new value.
target = mapbox::util::apply_visitor(evaluator, property.value);
} else {
// Do not apply this property because its transition hasn't begun yet.
}
}
}
}
template <typename T>
void calculateTransitioned(PropertyKey key, T& target, const StyleCalculationParameters& parameters) {
auto it = appliedStyle.find(key);
if (it != appliedStyle.end()) {
AppliedClassPropertyValues &applied = it->second;
// Iterate through all properties that we need to apply in order.
const PropertyEvaluator<T> evaluator(parameters);
for (auto& property : applied.propertyValues) {
if (parameters.now >= property.end) {
// We overwrite the current property with the new value.
target = mapbox::util::apply_visitor(evaluator, property.value);
} else if (parameters.now >= property.begin) {
// We overwrite the current property partially with the new value.
float progress = std::chrono::duration<float>(parameters.now - property.begin) / (property.end - property.begin);
target = util::interpolate(target, mapbox::util::apply_visitor(evaluator, property.value), progress);
hasPendingTransitions = true;
} else {
// Do not apply this property because its transition hasn't begun yet.
}
}
}
}
// Raw parsed paint class to property value map.
std::map<ClassID, ClassProperties> paints;
private:
// Applies all properties from a class, if they haven't been applied already.
void cascadeClass(const ClassID,
std::set<PropertyKey>&,
const TimePoint&,
const PropertyTransition&);
// For every property, stores a list of applied property values, with
// optional transition times.
std::map<PropertyKey, AppliedClassPropertyValues> appliedStyle;
// Stores whether there are pending transitions to be done on each update.
bool hasPendingTransitions = false;
};
};
#endif

View File

@@ -121,9 +121,9 @@ void Style::update(const TransformState& transform,
void Style::cascade() {
for (const auto& layer : layers) {
layer->setClasses(data.getClasses(),
data.getAnimationTime(),
PropertyTransition { data.getDefaultTransitionDuration(), data.getDefaultTransitionDelay() });
layer->cascade(data.getClasses(),
data.getAnimationTime(),
PropertyTransition { data.getDefaultTransitionDuration(), data.getDefaultTransitionDelay() });
}
}
@@ -142,7 +142,8 @@ void Style::recalculate(float z) {
data.getDefaultFadeDuration());
for (const auto& layer : layers) {
layer->updateProperties(parameters);
layer->recalculate(parameters);
if (!layer->bucket) {
continue;
}

View File

@@ -1,6 +1,4 @@
#include <mbgl/style/style_layer.hpp>
#include <mbgl/style/property_fallback.hpp>
#include <mbgl/layer/fill_layer.hpp>
#include <mbgl/layer/line_layer.hpp>
#include <mbgl/layer/circle_layer.hpp>
@@ -29,122 +27,18 @@ std::unique_ptr<StyleLayer> StyleLayer::create(StyleLayerType type) {
}
}
void StyleLayer::cascade(const std::vector<std::string>& classes,
const TimePoint& now,
const PropertyTransition& defaultTransition) {
paints.cascade(classes, now, defaultTransition);
}
bool StyleLayer::hasTransitions() const {
return paints.hasTransitions();
}
bool StyleLayer::hasRenderPass(RenderPass pass) const {
return bool(passes & pass);
}
void StyleLayer::setClasses(const std::vector<std::string> &class_names, const TimePoint& now,
const PropertyTransition &defaultTransition) {
// Stores all keys that we have already added transitions for.
std::set<PropertyKey> already_applied;
// We only apply the default style values if there are no classes set.
if (class_names.empty()) {
applyClassProperties(ClassID::Default, already_applied, now, defaultTransition);
return;
}
// Reverse iterate through all class names and apply them last to first.
for (auto it = class_names.rbegin(); it != class_names.rend(); ++it) {
const std::string &class_name = *it;
// From here on, we're only dealing with IDs to avoid comparing strings all the time.
const ClassID class_id = ClassDictionary::Get().lookup(class_name);
applyClassProperties(class_id, already_applied, now, defaultTransition);
}
// As the last class, apply the default class.
applyClassProperties(ClassID::Default, already_applied, now, defaultTransition);
// Make sure that we also transition to the fallback value for keys that aren't changed by
// any applied classes.
for (auto& property_pair : appliedStyle) {
const PropertyKey key = property_pair.first;
if (already_applied.find(key) != already_applied.end()) {
// This property has already been set by a previous class, so we don't need to
// transition to the fallback.
continue;
}
AppliedClassPropertyValues &appliedProperties = property_pair.second;
// Make sure that we don't do double transitions to the fallback value.
if (appliedProperties.mostRecent() == ClassID::Fallback) {
continue;
}
// This property key hasn't been set by a previous class, so we need to add a transition
// to the fallback value for that key.
const TimePoint begin = now + *defaultTransition.delay;
const TimePoint end = begin + *defaultTransition.duration;
const PropertyValue &value = PropertyFallbackValue::Get(key);
appliedProperties.add(ClassID::Fallback, begin, end, value);
}
}
// Helper function for applying all properties of a a single class that haven't been applied yet.
void StyleLayer::applyClassProperties(const ClassID class_id,
std::set<PropertyKey> &already_applied, const TimePoint& now,
const PropertyTransition &defaultTransition) {
auto style_it = styles.find(class_id);
if (style_it == styles.end()) {
// There is no class in this layer with this class_name.
return;
}
// Loop through all the properties in this style, and add transitions to them, if they're
// not already the most recent transition.
const ClassProperties &class_properties = style_it->second;
for (const auto& property_pair : class_properties) {
PropertyKey key = property_pair.first;
if (already_applied.find(key) != already_applied.end()) {
// This property has already been set by a previous class.
continue;
}
// Mark this property as written by a previous class, so that subsequent
// classes won't override this.
already_applied.insert(key);
// If the most recent transition is not the one with the highest priority, create
// a transition.
AppliedClassPropertyValues &appliedProperties = appliedStyle[key];
if (appliedProperties.mostRecent() != class_id) {
PropertyTransition transition = class_properties.getTransition(key);
Duration delay = transition.delay ? *transition.delay : *defaultTransition.delay;
Duration duration = transition.duration ? *transition.duration : *defaultTransition.duration;
const TimePoint begin = now + delay;
const TimePoint end = begin + duration;
const PropertyValue &value = property_pair.second;
appliedProperties.add(class_id, begin, end, value);
}
}
}
void StyleLayer::updateProperties(const StyleCalculationParameters& parameters) {
cleanupAppliedStyleProperties(parameters.now);
// Clear the pending transitions flag upon each update.
hasPendingTransitions = false;
// Update the render passes when this layer is visible.
passes = applyStyleProperties(parameters);
}
bool StyleLayer::hasTransitions() const {
for (const auto& pair : appliedStyle) {
if (pair.second.hasTransitions()) {
return true;
}
}
return hasPendingTransitions;
}
void StyleLayer::cleanupAppliedStyleProperties(const TimePoint& now) {
for (auto it = appliedStyle.begin(); it != appliedStyle.end();) {
AppliedClassPropertyValues& values = it->second;
values.cleanup(now);
// If the current properties object is empty, remove it from the map entirely.
values.empty() ? appliedStyle.erase(it++) : ++it;
}
}
}

View File

@@ -3,26 +3,23 @@
#include <mbgl/style/class_dictionary.hpp>
#include <mbgl/style/class_properties.hpp>
#include <mbgl/style/applied_class_properties.hpp>
#include <mbgl/style/zoom_history.hpp>
#include <mbgl/style/property_evaluator.hpp>
#include <mbgl/style/style_calculation_parameters.hpp>
#include <mbgl/style/paint_properties_map.hpp>
#include <mbgl/renderer/render_pass.hpp>
#include <mbgl/util/ptr.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/interpolate.hpp>
#include <mbgl/util/ptr.hpp>
#include <vector>
#include <string>
#include <map>
#include <set>
namespace mbgl {
class StyleBucket;
class StyleCalculationParameters;
class PropertyTransition;
class StyleLayer : public util::noncopyable {
public:
@@ -30,97 +27,35 @@ public:
virtual ~StyleLayer() = default;
// Partially evaluate paint properties based on a set of classes.
void cascade(const std::vector<std::string>& classNames,
const TimePoint& now,
const PropertyTransition& defaultTransition);
// Fully evaluate cascaded paint properties based on a zoom level.
virtual void recalculate(const StyleCalculationParameters&) = 0;
// Checks whether this layer has any active paint properties with transitions.
bool hasTransitions() const;
// Checks whether this layer needs to be rendered in the given render pass.
bool hasRenderPass(RenderPass) const;
// Updates the StyleProperties information in this layer by evaluating all
// pending transitions and applied classes in order.
void updateProperties(const StyleCalculationParameters&);
// Sets the list of classes and creates transitions to the currently applied values.
void setClasses(const std::vector<std::string> &class_names, const TimePoint& now,
const PropertyTransition &defaultTransition);
bool hasTransitions() const;
public:
// The name of this layer.
std::string id;
StyleLayerType type = StyleLayerType::Unknown;
// Bucket information, telling the renderer how to generate the geometries
// for this layer (feature property filters, tessellation instructions, ...).
util::ptr<StyleBucket> bucket;
// Contains all style classes that can be applied to this layer.
std::map<ClassID, ClassProperties> styles;
// Contains all paint classes that can be applied to this layer.
PaintPropertiesMap paints;
protected:
// TODO: extract
template <typename T>
void applyStyleProperty(PropertyKey key, T& target, const StyleCalculationParameters& parameters) {
auto it = appliedStyle.find(key);
if (it != appliedStyle.end()) {
AppliedClassPropertyValues &applied = it->second;
// Iterate through all properties that we need to apply in order.
const PropertyEvaluator<T> evaluator(parameters);
for (auto& property : applied.propertyValues) {
if (parameters.now >= property.begin) {
// We overwrite the current property with the new value.
target = mapbox::util::apply_visitor(evaluator, property.value);
} else {
// Do not apply this property because its transition hasn't begun yet.
}
}
}
}
template <typename T>
void applyTransitionedStyleProperty(PropertyKey key, T& target, const StyleCalculationParameters& parameters) {
auto it = appliedStyle.find(key);
if (it != appliedStyle.end()) {
AppliedClassPropertyValues &applied = it->second;
// Iterate through all properties that we need to apply in order.
const PropertyEvaluator<T> evaluator(parameters);
for (auto& property : applied.propertyValues) {
if (parameters.now >= property.end) {
// We overwrite the current property with the new value.
target = mapbox::util::apply_visitor(evaluator, property.value);
} else if (parameters.now >= property.begin) {
// We overwrite the current property partially with the new value.
float progress = std::chrono::duration<float>(parameters.now - property.begin) / (property.end - property.begin);
target = util::interpolate(target, mapbox::util::apply_visitor(evaluator, property.value), progress);
hasPendingTransitions = true;
} else {
// Do not apply this property because its transition hasn't begun yet.
}
}
}
}
private:
// Applies all properties from a class, if they haven't been applied already.
void applyClassProperties(ClassID class_id, std::set<PropertyKey> &already_applied,
const TimePoint& now, const PropertyTransition &defaultTransition);
// Sets the properties of this object by evaluating all pending transitions and
// aplied classes in order.
virtual RenderPass applyStyleProperties(const StyleCalculationParameters&) = 0;
// Removes all expired style transitions.
void cleanupAppliedStyleProperties(const TimePoint& now);
// For every property, stores a list of applied property values, with
// optional transition times.
std::map<PropertyKey, AppliedClassPropertyValues> appliedStyle;
// Stores what render passes this layer is currently enabled for. This depends on the
// evaluated StyleProperties object and is updated accordingly.
RenderPass passes = RenderPass::None;
// Stores whether there are pending transitions to be done on each update.
bool hasPendingTransitions = false;
};
}

View File

@@ -798,7 +798,7 @@ void StyleParser::parseLayer(const std::string& id, JSVal value, util::ptr<Style
std::map<ClassID, ClassProperties> paints;
parsePaints(value, paints);
layer->styles = std::move(paints);
layer->paints.paints = std::move(paints);
}
#pragma mark - Parse Styles