Files
MapLibreParticleLab/src/style/style.cpp
2014-05-27 12:08:36 +02:00

1014 lines
48 KiB
C++

#include <llmr/style/style.hpp>
#include <llmr/style/style_parser.hpp>
#include <llmr/util/constants.hpp>
#include <csscolorparser/csscolorparser.hpp>
#include <rapidjson/document.h>
using namespace llmr;
Style::Style() {
appliedClasses.insert("default");
}
void Style::reset() {
computed.fills.clear();
computed.lines.clear();
computed.icons.clear();
computed.texts.clear();
computed.rasters.clear();
computed.composites.clear();
computed.background.color = {{ 1, 1, 1, 1 }};
computed.background.opacity = 1.0;
properties_to_transition.clear();
}
void Style::cascadeProperties(GenericProperties &properties, const GenericClass& klass, const std::string& layer_name, const std::string& class_name, float z) {
if (klass.enabled) {
properties.enabled = klass.enabled.get().evaluate<bool>(z);
}
if (klass.translate) {
properties.translate = {{ klass.translate.get()[0].evaluate<float>(z),
klass.translate.get()[1].evaluate<float>(z) }};
computed.effective_classes[layer_name][TransitionablePropertyKey::Translate] = class_name;
if (klass.translate_transition && klass.translate_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Translate] = klass.translate_transition.get();
}
}
if (klass.translateAnchor) {
properties.translateAnchor = klass.translateAnchor.get();
}
if (klass.opacity) {
properties.opacity = klass.opacity.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Opacity] = class_name;
if (klass.opacity_transition && klass.opacity_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Opacity] = klass.opacity_transition.get();
}
}
if (klass.prerender) {
properties.prerender = klass.prerender.get();
}
if (klass.prerenderBuffer) {
properties.prerenderBuffer = klass.prerenderBuffer.get();
}
if (klass.prerenderSize) {
properties.prerenderSize = klass.prerenderSize.get();
}
if (klass.prerenderBlur) {
properties.prerenderBlur = klass.prerenderBlur.get();
}
}
void Style::cascade(float z) {
uv::writelock lock(mtx);
time start = util::now();
previous.fills.swap(computed.fills);
previous.lines.swap(computed.lines);
previous.icons.swap(computed.icons);
previous.texts.swap(computed.texts);
previous.rasters.swap(computed.rasters);
previous.composites.swap(computed.composites);
previous.background = computed.background;
previous.effective_classes.swap(computed.effective_classes);
reset();
// Accomodate for different tile size.
// TODO: Make this per-layer once individual layers have a specific tile size.
z += std::log(util::tileSize / 256.0f) / M_LN2;
// Recalculate style with basic cascading. Also store the last applied class
// for each property to assist in determining transitions.
for (const auto& class_pair : classes) {
const std::string& class_name = class_pair.first;
const ClassDescription& sheetClass = class_pair.second;
// Skip if not enabled.
if (appliedClasses.find(class_name) == appliedClasses.end()) continue;
// Cascade fill classes.
for (const auto& fill_pair : sheetClass.fill) {
const std::string& layer_name = fill_pair.first;
const llmr::FillClass& layer = fill_pair.second;
llmr::FillProperties& fill = computed.fills[layer_name];
cascadeProperties(fill, layer, layer_name, class_name, z);
if (layer.winding) {
fill.winding = layer.winding.get();
}
if (layer.antialias) {
fill.antialias = layer.antialias.get().evaluate<bool>(z);
}
if (layer.fill_color) {
fill.fill_color = layer.fill_color.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::FillColor] = class_name;
if (layer.fill_color_transition && layer.fill_color_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::FillColor] = layer.fill_color_transition.get();
}
}
if (layer.stroke_color) {
fill.stroke_color = layer.stroke_color.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::StrokeColor] = class_name;
if (layer.stroke_color_transition && layer.stroke_color_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::StrokeColor] = layer.stroke_color_transition.get();
}
}
if (layer.image) {
fill.image = layer.image.get();
}
}
// Cascade line classes.
for (const auto& line_pair : sheetClass.line) {
const std::string& layer_name = line_pair.first;
const llmr::LineClass& layer = line_pair.second;
llmr::LineProperties& stroke = computed.lines[layer_name];
cascadeProperties(stroke, layer, layer_name, class_name, z);
if (layer.width) {
stroke.width = layer.width.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Width] = class_name;
if (layer.width_transition && layer.width_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Width] = layer.width_transition.get();
}
}
if (layer.offset) {
stroke.offset = layer.offset.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Offset] = class_name;
if (layer.offset_transition && layer.offset_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Offset] = layer.offset_transition.get();
}
}
if (layer.color) {
stroke.color = layer.color.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
if (layer.color_transition && layer.color_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Color] = layer.color_transition.get();
}
}
if (layer.dash_array) {
stroke.dash_array = {{ layer.dash_array.get()[0].evaluate<float>(z),
layer.dash_array.get()[1].evaluate<float>(z) }};
computed.effective_classes[layer_name][TransitionablePropertyKey::DashArray] = class_name;
if (layer.dash_array_transition && layer.dash_array_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::DashArray] = layer.dash_array_transition.get();
}
}
}
// Cascade icon classes.
for (const auto& icon_pair : sheetClass.icon) {
const std::string& layer_name = icon_pair.first;
const llmr::IconClass& layer = icon_pair.second;
llmr::IconProperties& icon = computed.icons[layer_name];
cascadeProperties(icon, layer, layer_name, class_name, z);
if (layer.color) {
icon.color = layer.color.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
if (layer.color_transition && layer.color_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Color] = layer.color_transition.get();
}
}
if (layer.size) {
icon.size = layer.size.get().evaluate<float>(z);
}
if (layer.image) {
icon.image = layer.image.get();
}
if (layer.radius) {
icon.radius = layer.radius.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Radius] = class_name;
if (layer.radius_transition && layer.radius_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Radius] = layer.radius_transition.get();
}
}
if (layer.blur) {
icon.blur = layer.blur.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::Blur] = class_name;
if (layer.blur_transition && layer.blur_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Blur] = layer.blur_transition.get();
}
}
}
// Cascade text classes.
for (const auto& text_pair : sheetClass.text) {
const std::string& layer_name = text_pair.first;
const llmr::TextClass& layer = text_pair.second;
llmr::TextProperties& text = computed.texts[layer_name];
cascadeProperties(text, layer, layer_name, class_name, z);
if (layer.color) {
text.color = layer.color.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::Color] = class_name;
if (layer.color_transition && layer.color_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Color] = layer.color_transition.get();
}
}
if (layer.size) {
text.size = layer.size.get().evaluate<float>(z);
}
if (layer.halo) {
text.halo = layer.halo.get();
computed.effective_classes[layer_name][TransitionablePropertyKey::Halo] = class_name;
if (layer.halo_transition && layer.halo_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::Halo] = layer.halo_transition.get();
}
}
if (layer.halo_radius) {
text.halo_radius = layer.halo_radius.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::HaloRadius] = class_name;
if (layer.halo_radius_transition && layer.halo_radius_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::HaloRadius] = layer.halo_radius_transition.get();
}
}
if (layer.halo_blur) {
text.halo_blur = layer.halo_blur.get().evaluate<float>(z);
computed.effective_classes[layer_name][TransitionablePropertyKey::HaloBlur] = class_name;
if (layer.halo_blur_transition && layer.halo_blur_transition.get().duration) {
properties_to_transition[layer_name][TransitionablePropertyKey::HaloBlur] = layer.halo_blur_transition.get();
}
}
if (layer.rotate) {
text.rotate = layer.rotate.get().evaluate<float>(z);
}
if (layer.always_visible) {
text.always_visible = layer.always_visible.get().evaluate<bool>(z);
}
}
// Cascade raster classes.
for (const auto& raster_pair : sheetClass.raster) {
const std::string& layer_name = raster_pair.first;
const llmr::RasterClass& layer = raster_pair.second;
llmr::RasterProperties& raster = computed.rasters[layer_name];
cascadeProperties(raster, layer, layer_name, class_name, z);
}
// Cascade composite classes.
for (const auto& composite_pair : sheetClass.composite) {
const std::string& layer_name = composite_pair.first;
const llmr::CompositeClass& layer = composite_pair.second;
llmr::CompositeProperties& composite = computed.composites[layer_name];
cascadeProperties(composite, layer, layer_name, class_name, z);
}
// Cascade background.
{
cascadeProperties(computed.background, sheetClass.background, "background", class_name, z);
if (sheetClass.background.color) {
computed.background.color = sheetClass.background.color.get();
computed.effective_classes["background"][TransitionablePropertyKey::Color] = class_name;
if (sheetClass.background.color_transition && sheetClass.background.color_transition.get().duration) {
properties_to_transition["background"][TransitionablePropertyKey::Color] = sheetClass.background.color_transition.get();
}
}
}
}
// Apply transitions after the first time.
if (!initial_render_complete) {
initial_render_complete = true;
return;
}
// Fills
for (const auto& fill_pair : computed.fills) {
const std::string& layer_name = fill_pair.first;
// translate
if (transitionInProgress(layer_name, TransitionablePropertyKey::Translate, start)) {
computed.fills[layer_name].translate = transitioning.fills[layer_name].translate;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Translate)) {
transitioning.fills[layer_name].translate = previous.fills[layer_name].translate;
transitions[layer_name][TransitionablePropertyKey::Translate] =
std::make_shared<util::ease_transition<std::array<float, 2>>>(previous.fills[layer_name].translate,
computed.fills[layer_name].translate,
transitioning.fills[layer_name].translate,
start + transitionDelay(layer_name, TransitionablePropertyKey::Translate),
transitionDuration(layer_name, TransitionablePropertyKey::Translate));
computed.fills[layer_name].translate = transitioning.fills[layer_name].translate;
}
// fill color
if (transitionInProgress(layer_name, TransitionablePropertyKey::FillColor, start)) {
computed.fills[layer_name].fill_color = transitioning.fills[layer_name].fill_color;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::FillColor)) {
transitioning.fills[layer_name].fill_color = previous.fills[layer_name].fill_color;
transitions[layer_name][TransitionablePropertyKey::FillColor] =
std::make_shared<util::ease_transition<Color>>(previous.fills[layer_name].fill_color,
computed.fills[layer_name].fill_color,
transitioning.fills[layer_name].fill_color,
start + transitionDelay(layer_name, TransitionablePropertyKey::FillColor),
transitionDuration(layer_name, TransitionablePropertyKey::FillColor));
computed.fills[layer_name].fill_color = transitioning.fills[layer_name].fill_color;
}
// stroke color
if (transitionInProgress(layer_name, TransitionablePropertyKey::StrokeColor, start)) {
computed.fills[layer_name].stroke_color = transitioning.fills[layer_name].stroke_color;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::StrokeColor)) {
transitioning.fills[layer_name].stroke_color = previous.fills[layer_name].stroke_color;
transitions[layer_name][TransitionablePropertyKey::StrokeColor] =
std::make_shared<util::ease_transition<Color>>(previous.fills[layer_name].stroke_color,
computed.fills[layer_name].stroke_color,
transitioning.fills[layer_name].stroke_color,
start + transitionDelay(layer_name, TransitionablePropertyKey::StrokeColor),
transitionDuration(layer_name, TransitionablePropertyKey::StrokeColor));
computed.fills[layer_name].stroke_color = transitioning.fills[layer_name].stroke_color;
}
// opacity
if (transitionInProgress(layer_name, TransitionablePropertyKey::Opacity, start)) {
computed.fills[layer_name].opacity = transitioning.fills[layer_name].opacity;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Opacity)) {
transitioning.fills[layer_name].opacity = previous.fills[layer_name].opacity;
transitions[layer_name][TransitionablePropertyKey::Opacity] =
std::make_shared<util::ease_transition<float>>(previous.fills[layer_name].opacity,
computed.fills[layer_name].opacity,
transitioning.fills[layer_name].opacity,
start + transitionDelay(layer_name, TransitionablePropertyKey::Opacity),
transitionDuration(layer_name, TransitionablePropertyKey::Opacity));
computed.fills[layer_name].opacity = transitioning.fills[layer_name].opacity;
}
}
// Lines
for (const auto& line_pair : computed.lines) {
const std::string& layer_name = line_pair.first;
// translate
if (transitionInProgress(layer_name, TransitionablePropertyKey::Translate, start)) {
computed.lines[layer_name].translate = transitioning.lines[layer_name].translate;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Translate)) {
transitioning.lines[layer_name].translate = previous.lines[layer_name].translate;
transitions[layer_name][TransitionablePropertyKey::Translate] =
std::make_shared<util::ease_transition<std::array<float, 2>>>(previous.lines[layer_name].translate,
computed.lines[layer_name].translate,
transitioning.lines[layer_name].translate,
start + transitionDelay(layer_name, TransitionablePropertyKey::Translate),
transitionDuration(layer_name, TransitionablePropertyKey::Translate));
computed.lines[layer_name].translate = transitioning.lines[layer_name].translate;
}
// width
if (transitionInProgress(layer_name, TransitionablePropertyKey::Width, start)) {
computed.lines[layer_name].width = transitioning.lines[layer_name].width;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Width)) {
transitioning.lines[layer_name].width = previous.lines[layer_name].width;
transitions[layer_name][TransitionablePropertyKey::Width] =
std::make_shared<util::ease_transition<float>>(previous.lines[layer_name].width,
computed.lines[layer_name].width,
transitioning.lines[layer_name].width,
start + transitionDelay(layer_name, TransitionablePropertyKey::Width),
transitionDuration(layer_name, TransitionablePropertyKey::Width));
computed.lines[layer_name].width = transitioning.lines[layer_name].width;
}
// offset
if (transitionInProgress(layer_name, TransitionablePropertyKey::Offset, start)) {
computed.lines[layer_name].offset = transitioning.lines[layer_name].offset;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Offset)) {
transitioning.lines[layer_name].offset = previous.lines[layer_name].offset;
transitions[layer_name][TransitionablePropertyKey::Offset] =
std::make_shared<util::ease_transition<float>>(previous.lines[layer_name].offset,
computed.lines[layer_name].offset,
transitioning.lines[layer_name].offset,
start + transitionDelay(layer_name, TransitionablePropertyKey::Offset),
transitionDuration(layer_name, TransitionablePropertyKey::Offset));
computed.lines[layer_name].offset = transitioning.lines[layer_name].offset;
}
// color
if (transitionInProgress(layer_name, TransitionablePropertyKey::Color, start)) {
computed.lines[layer_name].color = transitioning.lines[layer_name].color;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Color)) {
transitioning.lines[layer_name].color = previous.lines[layer_name].color;
transitions[layer_name][TransitionablePropertyKey::Color] =
std::make_shared<util::ease_transition<Color>>(previous.lines[layer_name].color,
computed.lines[layer_name].color,
transitioning.lines[layer_name].color,
start + transitionDelay(layer_name, TransitionablePropertyKey::Color),
transitionDuration(layer_name, TransitionablePropertyKey::Color));
computed.lines[layer_name].color = transitioning.lines[layer_name].color;
}
// dasharray
if (transitionInProgress(layer_name, TransitionablePropertyKey::DashArray, start)) {
computed.lines[layer_name].dash_array = transitioning.lines[layer_name].dash_array;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::DashArray)) {
transitioning.lines[layer_name].dash_array = previous.lines[layer_name].dash_array;
transitions[layer_name][TransitionablePropertyKey::DashArray] =
std::make_shared<util::ease_transition<std::array<float, 2>>>(previous.lines[layer_name].dash_array,
computed.lines[layer_name].dash_array,
transitioning.lines[layer_name].dash_array,
start + transitionDelay(layer_name, TransitionablePropertyKey::DashArray),
transitionDuration(layer_name, TransitionablePropertyKey::DashArray));
computed.lines[layer_name].dash_array = transitioning.lines[layer_name].dash_array;
}
// opacity
if (transitionInProgress(layer_name, TransitionablePropertyKey::Opacity, start)) {
computed.lines[layer_name].opacity = transitioning.lines[layer_name].opacity;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Opacity)) {
transitioning.lines[layer_name].opacity = previous.lines[layer_name].opacity;
transitions[layer_name][TransitionablePropertyKey::Opacity] =
std::make_shared<util::ease_transition<float>>(previous.lines[layer_name].opacity,
computed.lines[layer_name].opacity,
transitioning.lines[layer_name].opacity,
start + transitionDelay(layer_name, TransitionablePropertyKey::Opacity),
transitionDuration(layer_name, TransitionablePropertyKey::Opacity));
computed.lines[layer_name].opacity = transitioning.lines[layer_name].opacity;
}
}
// Icons
for (const auto& icon_pair : computed.icons) {
const std::string& layer_name = icon_pair.first;
// translate
if (transitionInProgress(layer_name, TransitionablePropertyKey::Translate, start)) {
computed.icons[layer_name].translate = transitioning.icons[layer_name].translate;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Translate)) {
transitioning.icons[layer_name].translate = previous.icons[layer_name].translate;
transitions[layer_name][TransitionablePropertyKey::Translate] =
std::make_shared<util::ease_transition<std::array<float, 2>>>(previous.icons[layer_name].translate,
computed.icons[layer_name].translate,
transitioning.icons[layer_name].translate,
start + transitionDelay(layer_name, TransitionablePropertyKey::Translate),
transitionDuration(layer_name, TransitionablePropertyKey::Translate));
computed.icons[layer_name].translate = transitioning.icons[layer_name].translate;
}
// color
if (transitionInProgress(layer_name, TransitionablePropertyKey::Color, start)) {
computed.icons[layer_name].color = transitioning.icons[layer_name].color;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Color)) {
transitioning.icons[layer_name].color = previous.icons[layer_name].color;
transitions[layer_name][TransitionablePropertyKey::Color] =
std::make_shared<util::ease_transition<Color>>(previous.icons[layer_name].color,
computed.icons[layer_name].color,
transitioning.icons[layer_name].color,
start + transitionDelay(layer_name, TransitionablePropertyKey::Color),
transitionDuration(layer_name, TransitionablePropertyKey::Color));
computed.icons[layer_name].color = transitioning.icons[layer_name].color;
}
// opacity
if (transitionInProgress(layer_name, TransitionablePropertyKey::Opacity, start)) {
computed.icons[layer_name].opacity = transitioning.icons[layer_name].opacity;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Opacity)) {
transitioning.icons[layer_name].opacity = previous.icons[layer_name].opacity;
transitions[layer_name][TransitionablePropertyKey::Opacity] =
std::make_shared<util::ease_transition<float>>(previous.icons[layer_name].opacity,
computed.icons[layer_name].opacity,
transitioning.icons[layer_name].opacity,
start + transitionDelay(layer_name, TransitionablePropertyKey::Opacity),
transitionDuration(layer_name, TransitionablePropertyKey::Opacity));
computed.icons[layer_name].opacity = transitioning.icons[layer_name].opacity;
}
// radius
if (transitionInProgress(layer_name, TransitionablePropertyKey::Radius, start)) {
computed.icons[layer_name].radius = transitioning.icons[layer_name].radius;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Radius)) {
transitioning.icons[layer_name].radius = previous.icons[layer_name].radius;
transitions[layer_name][TransitionablePropertyKey::Radius] =
std::make_shared<util::ease_transition<float>>(previous.icons[layer_name].radius,
computed.icons[layer_name].radius,
transitioning.icons[layer_name].radius,
start + transitionDelay(layer_name, TransitionablePropertyKey::Radius),
transitionDuration(layer_name, TransitionablePropertyKey::Radius));
computed.icons[layer_name].radius = transitioning.icons[layer_name].radius;
}
// blur
if (transitionInProgress(layer_name, TransitionablePropertyKey::Blur, start)) {
computed.icons[layer_name].blur = transitioning.icons[layer_name].blur;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Blur)) {
transitioning.icons[layer_name].blur = previous.icons[layer_name].blur;
transitions[layer_name][TransitionablePropertyKey::Blur] =
std::make_shared<util::ease_transition<float>>(previous.icons[layer_name].blur,
computed.icons[layer_name].blur,
transitioning.icons[layer_name].blur,
start + transitionDelay(layer_name, TransitionablePropertyKey::Blur),
transitionDuration(layer_name, TransitionablePropertyKey::Blur));
computed.icons[layer_name].blur = transitioning.icons[layer_name].blur;
}
}
// Text
for (const auto& text_pair : computed.texts) {
const std::string& layer_name = text_pair.first;
// translate
if (transitionInProgress(layer_name, TransitionablePropertyKey::Translate, start)) {
computed.texts[layer_name].translate = transitioning.texts[layer_name].translate;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Translate)) {
transitioning.texts[layer_name].translate = previous.texts[layer_name].translate;
transitions[layer_name][TransitionablePropertyKey::Translate] =
std::make_shared<util::ease_transition<std::array<float, 2>>>(previous.texts[layer_name].translate,
computed.texts[layer_name].translate,
transitioning.texts[layer_name].translate,
start + transitionDelay(layer_name, TransitionablePropertyKey::Translate),
transitionDuration(layer_name, TransitionablePropertyKey::Translate));
computed.texts[layer_name].translate = transitioning.texts[layer_name].translate;
}
// color
if (transitionInProgress(layer_name, TransitionablePropertyKey::Color, start)) {
computed.texts[layer_name].color = transitioning.texts[layer_name].color;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Color)) {
transitioning.texts[layer_name].color = previous.texts[layer_name].color;
transitions[layer_name][TransitionablePropertyKey::Color] =
std::make_shared<util::ease_transition<Color>>(previous.texts[layer_name].color,
computed.texts[layer_name].color,
transitioning.texts[layer_name].color,
start + transitionDelay(layer_name, TransitionablePropertyKey::Color),
transitionDuration(layer_name, TransitionablePropertyKey::Color));
computed.texts[layer_name].color = transitioning.texts[layer_name].color;
}
// halo
if (transitionInProgress(layer_name, TransitionablePropertyKey::Halo, start)) {
computed.texts[layer_name].halo = transitioning.texts[layer_name].halo;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Halo)) {
transitioning.texts[layer_name].halo = previous.texts[layer_name].halo;
transitions[layer_name][TransitionablePropertyKey::Halo] =
std::make_shared<util::ease_transition<Color>>(previous.texts[layer_name].halo,
computed.texts[layer_name].halo,
transitioning.texts[layer_name].halo,
start + transitionDelay(layer_name, TransitionablePropertyKey::Halo),
transitionDuration(layer_name, TransitionablePropertyKey::Halo));
computed.texts[layer_name].halo = transitioning.texts[layer_name].halo;
}
// halo radius
if (transitionInProgress(layer_name, TransitionablePropertyKey::HaloRadius, start)) {
computed.texts[layer_name].halo_radius = transitioning.texts[layer_name].halo_radius;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::HaloRadius)) {
transitioning.texts[layer_name].halo_radius = previous.texts[layer_name].halo_radius;
transitions[layer_name][TransitionablePropertyKey::HaloRadius] =
std::make_shared<util::ease_transition<float>>(previous.texts[layer_name].halo_radius,
computed.texts[layer_name].halo_radius,
transitioning.texts[layer_name].halo_radius,
start + transitionDelay(layer_name, TransitionablePropertyKey::HaloRadius),
transitionDuration(layer_name, TransitionablePropertyKey::HaloRadius));
computed.texts[layer_name].halo_radius = transitioning.texts[layer_name].halo_radius;
}
// halo blur
if (transitionInProgress(layer_name, TransitionablePropertyKey::HaloBlur, start)) {
computed.texts[layer_name].halo_blur = transitioning.texts[layer_name].halo_blur;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::HaloBlur)) {
transitioning.texts[layer_name].halo_blur = previous.texts[layer_name].halo_blur;
transitions[layer_name][TransitionablePropertyKey::HaloBlur] =
std::make_shared<util::ease_transition<float>>(previous.texts[layer_name].halo_blur,
computed.texts[layer_name].halo_blur,
transitioning.texts[layer_name].halo_blur,
start + transitionDelay(layer_name, TransitionablePropertyKey::HaloBlur),
transitionDuration(layer_name, TransitionablePropertyKey::HaloBlur));
computed.texts[layer_name].halo_blur = transitioning.texts[layer_name].halo_blur;
}
// opacity
if (transitionInProgress(layer_name, TransitionablePropertyKey::Opacity, start)) {
computed.texts[layer_name].opacity = transitioning.texts[layer_name].opacity;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Opacity)) {
transitioning.texts[layer_name].opacity = previous.texts[layer_name].opacity;
transitions[layer_name][TransitionablePropertyKey::Opacity] =
std::make_shared<util::ease_transition<float>>(previous.texts[layer_name].opacity,
computed.texts[layer_name].opacity,
transitioning.texts[layer_name].opacity,
start + transitionDelay(layer_name, TransitionablePropertyKey::Opacity),
transitionDuration(layer_name, TransitionablePropertyKey::Opacity));
computed.texts[layer_name].opacity = transitioning.texts[layer_name].opacity;
}
}
// Rasters
for (const auto& raster_pair : computed.rasters) {
const std::string& layer_name = raster_pair.first;
// opacity
if (transitionInProgress(layer_name, TransitionablePropertyKey::Opacity, start)) {
computed.rasters[layer_name].opacity = transitioning.rasters[layer_name].opacity;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Opacity)) {
transitioning.rasters[layer_name].opacity = previous.rasters[layer_name].opacity;
transitions[layer_name][TransitionablePropertyKey::Opacity] =
std::make_shared<util::ease_transition<float>>(previous.rasters[layer_name].opacity,
computed.rasters[layer_name].opacity,
transitioning.rasters[layer_name].opacity,
start + transitionDelay(layer_name, TransitionablePropertyKey::Opacity),
transitionDuration(layer_name, TransitionablePropertyKey::Opacity));
computed.rasters[layer_name].opacity = transitioning.rasters[layer_name].opacity;
}
}
// Composites
for (const auto& composite_pair : computed.composites) {
const std::string& layer_name = composite_pair.first;
// opacity
if (transitionInProgress(layer_name, TransitionablePropertyKey::Opacity, start)) {
computed.composites[layer_name].opacity = transitioning.composites[layer_name].opacity;
} else if (inNeedOfTransition(layer_name, TransitionablePropertyKey::Opacity)) {
transitioning.composites[layer_name].opacity = previous.composites[layer_name].opacity;
transitions[layer_name][TransitionablePropertyKey::Opacity] =
std::make_shared<util::ease_transition<float>>(previous.composites[layer_name].opacity,
computed.composites[layer_name].opacity,
transitioning.composites[layer_name].opacity,
start + transitionDelay(layer_name, TransitionablePropertyKey::Opacity),
transitionDuration(layer_name, TransitionablePropertyKey::Opacity));
computed.composites[layer_name].opacity = transitioning.composites[layer_name].opacity;
}
}
// Background
{
// color
if (transitionInProgress("background", TransitionablePropertyKey::Color, start)) {
computed.background.color = transitioning.background.color;
} else if (inNeedOfTransition("background", TransitionablePropertyKey::Color)) {
transitioning.background.color = previous.background.color;
transitions["background"][TransitionablePropertyKey::Color] =
std::make_shared<util::ease_transition<Color>>(previous.background.color,
computed.background.color,
transitioning.background.color,
start + transitionDelay("background", TransitionablePropertyKey::Color),
transitionDuration("background", TransitionablePropertyKey::Color));
computed.background.color = transitioning.background.color;
}
// opacity
if (transitionInProgress("background", TransitionablePropertyKey::Opacity, start)) {
computed.background.opacity = transitioning.background.opacity;
} else if (inNeedOfTransition("background", TransitionablePropertyKey::Opacity)) {
transitioning.background.opacity = previous.background.opacity;
transitions["background"][TransitionablePropertyKey::Opacity] =
std::make_shared<util::ease_transition<float>>(previous.background.opacity,
computed.background.opacity,
transitioning.background.opacity,
start + transitionDelay("background", TransitionablePropertyKey::Opacity),
transitionDuration("background", TransitionablePropertyKey::Opacity));
computed.background.opacity = transitioning.background.opacity;
}
}
}
bool Style::transitionInProgress(const std::string &layer_name, TransitionablePropertyKey key,
time start) {
const auto it = transitions.find(layer_name);
if (it == transitions.end()) {
return false;
}
const auto &const_transition = it->second;
const auto const_transition_it = const_transition.find(key);
if (const_transition_it == const_transition.end()) {
return false;
}
return const_transition_it->second->update(start) != util::transition::complete;
}
bool Style::transitionExists(const std::string &layer_name, TransitionablePropertyKey key) const {
const auto it = transitions.find(layer_name);
if (it == transitions.end()) {
return false;
}
const auto &const_transition = it->second;
return const_transition.find(key) != const_transition.end();
}
static const std::string empty_class_name;
inline const std::string &
find_effective_class(const std::unordered_map<
std::string, std::unordered_map<TransitionablePropertyKey, std::string>> &
effective_class_keys,
const std::string &layer_name, TransitionablePropertyKey key) {
const auto effective_class_keys_it = effective_class_keys.find(layer_name);
if (effective_class_keys_it != effective_class_keys.end()) {
const auto &effective_classes = effective_class_keys_it->second;
const auto effective_class_it = effective_classes.find(key);
if (effective_class_it != effective_classes.end()) {
return effective_class_it->second;
}
}
return empty_class_name;
}
bool Style::inNeedOfTransition(const std::string &layer_name, TransitionablePropertyKey key) const {
if (!transitionDuration(layer_name, key)) {
return false;
} else if (transitionExists(layer_name, key)) {
return false;
}
// This is a const version of this code:
// return (computed.effective_classes[layer_name][key] !=
// previous.effective_classes[layer_name][key]);
const std::string &computed_class =
find_effective_class(computed.effective_classes, layer_name, key);
const std::string &previous_class =
find_effective_class(previous.effective_classes, layer_name, key);
return computed_class != previous_class;
}
uint64_t Style::transitionDuration(const std::string &layer_name,
TransitionablePropertyKey key) const {
const auto it = properties_to_transition.find(layer_name);
if (it == properties_to_transition.end()) {
return default_transition_duration * 1_millisecond;
}
const auto &const_properties_to_transition = it->second;
const auto it2 = const_properties_to_transition.find(key);
if (it2 != const_properties_to_transition.end()) {
return it2->second.duration * 1_millisecond;
} else {
return default_transition_duration * 1_millisecond;
}
}
uint64_t Style::transitionDelay(const std::string &layer_name,
TransitionablePropertyKey key) const {
const auto it = properties_to_transition.find(layer_name);
if (it == properties_to_transition.end()) {
return 0;
}
const auto &const_properties_to_transition = it->second;
const auto it2 = const_properties_to_transition.find(key);
if (it2 != const_properties_to_transition.end()) {
return it2->second.delay * 1_millisecond;
} else {
return 0;
}
}
bool Style::needsTransition() const {
uv::readlock lock(mtx);
for (auto layer_it = transitions.begin(); layer_it != transitions.end(); layer_it++) {
auto& layer_transitions = layer_it->second;
if (layer_transitions.size()) {
return true;
}
}
return false;
}
void Style::updateTransitions(time now) {
uv::writelock lock(mtx);
for (auto layer_it = transitions.begin(); layer_it != transitions.end(); layer_it++) {
auto& layer_transitions = layer_it->second;
for (auto prop_it = layer_transitions.begin(); prop_it != layer_transitions.end();/**/) {
auto& transition = prop_it->second;
if (transition->update(now) == util::transition::complete) {
prop_it = layer_transitions.erase(prop_it);
} else {
prop_it++;
}
}
}
}
void Style::cancelTransitions() {
uv::writelock lock(mtx);
transitions.clear();
}
size_t Style::layerCount() const {
size_t count = 0;
for (const LayerDescription &layer : layers) {
count += layer.size();
}
return count;
}
void Style::setDefaultTransitionDuration(uint64_t duration) {
default_transition_duration = duration;
}
void Style::loadJSON(const uint8_t *const data, size_t bytes) {
rapidjson::Document doc;
if (bytes <= 0) {
return;
}
if (data[bytes - 1] != 0) {
throw exception("style JSON string is not 0-terminated");
}
doc.Parse<0>((const char *const)data);
const rapidjson::Document& document = doc;
buckets.clear();
layers.clear();
classes.clear();
StyleParser parser;
if (document.IsObject()) {
if (document.HasMember("buckets")) {
parser.parseBuckets(document["buckets"], buckets);
}
if (document.HasMember("structure")) {
parser.parseLayers(document["structure"], layers);
}
if (document.HasMember("constants")) {
parser.parseConstants(document["constants"]);
}
if (document.HasMember("classes")) {
parser.parseClasses(document["classes"], classes, buckets, layers);
}
} else {
throw Style::exception("stylesheet root must be an object");
}
}