#include #include #include #include #include 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(z); } if (klass.translate) { properties.translate = {{ klass.translate.get()[0].evaluate(z), klass.translate.get()[1].evaluate(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(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(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(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(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(z), layer.dash_array.get()[1].evaluate(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(z); } if (layer.image) { icon.image = layer.image.get(); } if (layer.radius) { icon.radius = layer.radius.get().evaluate(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(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(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(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(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(z); } if (layer.always_visible) { text.always_visible = layer.always_visible.get().evaluate(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>>(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>(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>(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>(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>>(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>(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>(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>(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>>(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>(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>>(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>(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>(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>(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>(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>>(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>(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>(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>(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>(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>(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>(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>(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>(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>(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> & 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"); } }