From 4e7bc29028dc5f7048546f3f8f06a737d215cefb Mon Sep 17 00:00:00 2001 From: suddenly Date: Tue, 28 Feb 2023 01:15:19 +0500 Subject: [PATCH 01/11] Update ElementAnimation.cpp --- Source/Core/ElementAnimation.cpp | 49 +++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/Source/Core/ElementAnimation.cpp b/Source/Core/ElementAnimation.cpp index 241050f2e..7541e0695 100644 --- a/Source/Core/ElementAnimation.cpp +++ b/Source/Core/ElementAnimation.cpp @@ -165,6 +165,53 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f return Property{ TransformPtr(std::move(t)), Property::TRANSFORM }; } + if (p0.unit == Property::DECORATOR && p1.unit == Property::DECORATOR) + { + auto& d0 = p0.value.GetReference(); + auto& d1 = p1.value.GetReference(); + + if (d0->list.size() != d1->list.size()) + { + RMLUI_ERRORMSG("The number of decorators should be the same."); + return Property{ d0, Property::DECORATOR }; + } + + UniquePtr decorators(new DecoratorDeclarationList); + + for(int i = 0; i < d0->list.size(); i++) { + const DecoratorDeclaration& declaration1 = d0->list[i]; + const DecoratorDeclaration& declaration2 = d1->list[i]; + + if (declaration1.type != declaration2.type) + { + RMLUI_ERRORMSG("The types of decorators should be the same."); + return Property{ d0, Property::DECORATOR }; + } + + if (declaration1.instancer != declaration2.instancer) + { + RMLUI_ERRORMSG("The instancers of decorators should be the same."); + return Property{ d0, Property::DECORATOR }; + } + + decorators->value.append(declaration1.type).append(" "); + + PropertyDictionary result_roperties; + declaration1.instancer->GetPropertySpecification().SetPropertyDefaults(result_roperties); + + const PropertyMap& properties1 = declaration1.properties.GetProperties(); + const PropertyMap& properties2 = declaration2.properties.GetProperties(); + for (auto it1 = properties1.begin(), it2 = properties2.begin(); it1 != properties1.end() || it2 != properties2.end(); it1++, it2++) + { + result_roperties.SetProperty(it1->first, InterpolateProperties(it1->second, it2->second, alpha, element, it1->second.definition)); + decorators->value.append(result_roperties.GetProperty(it1->first)->Get()).append(" "); + } + decorators->list.push_back(DecoratorDeclaration{declaration1.type, declaration1.instancer, result_roperties}); + } + + return Property{ DecoratorsPtr(std::move(decorators)), Property::DECORATOR }; + } + // Fall back to discrete interpolation for incompatible units. return alpha < 0.5f ? p0 : p1; } @@ -409,7 +456,7 @@ ElementAnimation::ElementAnimation(PropertyId property_id, ElementAnimationOrigi bool ElementAnimation::InternalAddKey(float time, const Property& in_property, Element& element, Tween tween) { - int valid_properties = (Property::NUMBER_LENGTH_PERCENT | Property::ANGLE | Property::COLOUR | Property::TRANSFORM | Property::KEYWORD); + int valid_properties = (Property::NUMBER_LENGTH_PERCENT | Property::ANGLE | Property::COLOUR | Property::TRANSFORM | Property::KEYWORD | Property::DECORATOR); if (!(in_property.unit & valid_properties)) { From 14b40d20df9992d22edd056c7965f8a1cbabf2b2 Mon Sep 17 00:00:00 2001 From: suddenly Date: Tue, 28 Feb 2023 02:45:24 +0500 Subject: [PATCH 02/11] Removed caching of decorators for animation. --- Include/RmlUi/Core/StyleSheet.h | 2 +- Include/RmlUi/Core/StyleSheetTypes.h | 1 + Source/Core/ElementAnimation.cpp | 21 ++++++------- Source/Core/ElementDecoration.cpp | 3 +- Source/Core/PropertyParserDecorator.cpp | 1 + Source/Core/StyleSheet.cpp | 39 +++++++++++++++---------- 6 files changed, 37 insertions(+), 30 deletions(-) diff --git a/Include/RmlUi/Core/StyleSheet.h b/Include/RmlUi/Core/StyleSheet.h index 5409ad542..dc95563c2 100644 --- a/Include/RmlUi/Core/StyleSheet.h +++ b/Include/RmlUi/Core/StyleSheet.h @@ -78,7 +78,7 @@ class RMLUICORE_API StyleSheet final : public NonCopyMoveable SharedPtr GetElementDefinition(const Element* element) const; /// Returns a list of instanced decorators from the declarations. The instances are cached for faster future retrieval. - const Vector>& InstanceDecorators(const DecoratorDeclarationList& declaration_list, const PropertySource* decorator_source) const; + void InstanceDecorators(Vector>& decorator_list, const DecoratorDeclarationList& declaration_list, const PropertySource* decorator_source) const; private: StyleSheet(); diff --git a/Include/RmlUi/Core/StyleSheetTypes.h b/Include/RmlUi/Core/StyleSheetTypes.h index fde4dba84..7da3e1cbf 100644 --- a/Include/RmlUi/Core/StyleSheetTypes.h +++ b/Include/RmlUi/Core/StyleSheetTypes.h @@ -66,6 +66,7 @@ struct DecoratorDeclaration { struct DecoratorDeclarationList { Vector list; String value; + bool caching; }; struct MediaBlock { diff --git a/Source/Core/ElementAnimation.cpp b/Source/Core/ElementAnimation.cpp index 7541e0695..26abb7726 100644 --- a/Source/Core/ElementAnimation.cpp +++ b/Source/Core/ElementAnimation.cpp @@ -172,44 +172,41 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f if (d0->list.size() != d1->list.size()) { - RMLUI_ERRORMSG("The number of decorators should be the same."); + RMLUI_ERRORMSG("The number of decorators should be the same"); return Property{ d0, Property::DECORATOR }; } - UniquePtr decorators(new DecoratorDeclarationList); + UniquePtr declaration_list(new DecoratorDeclarationList); + declaration_list->caching = false; - for(int i = 0; i < d0->list.size(); i++) { + for (int i = 0; i < d0->list.size(); i++) { const DecoratorDeclaration& declaration1 = d0->list[i]; const DecoratorDeclaration& declaration2 = d1->list[i]; if (declaration1.type != declaration2.type) { - RMLUI_ERRORMSG("The types of decorators should be the same."); + RMLUI_ERRORMSG("The types of decorators should be the same"); return Property{ d0, Property::DECORATOR }; } if (declaration1.instancer != declaration2.instancer) { - RMLUI_ERRORMSG("The instancers of decorators should be the same."); + RMLUI_ERRORMSG("The instancers of decorators should be the same"); return Property{ d0, Property::DECORATOR }; } - - decorators->value.append(declaration1.type).append(" "); - PropertyDictionary result_roperties; declaration1.instancer->GetPropertySpecification().SetPropertyDefaults(result_roperties); - + const PropertyMap& properties1 = declaration1.properties.GetProperties(); const PropertyMap& properties2 = declaration2.properties.GetProperties(); for (auto it1 = properties1.begin(), it2 = properties2.begin(); it1 != properties1.end() || it2 != properties2.end(); it1++, it2++) { result_roperties.SetProperty(it1->first, InterpolateProperties(it1->second, it2->second, alpha, element, it1->second.definition)); - decorators->value.append(result_roperties.GetProperty(it1->first)->Get()).append(" "); } - decorators->list.push_back(DecoratorDeclaration{declaration1.type, declaration1.instancer, result_roperties}); + declaration_list->list.push_back(DecoratorDeclaration{ declaration1.type, declaration1.instancer, result_roperties }); } - return Property{ DecoratorsPtr(std::move(decorators)), Property::DECORATOR }; + return Property{ DecoratorsPtr(std::move(declaration_list)), Property::DECORATOR }; } // Fall back to discrete interpolation for incompatible units. diff --git a/Source/Core/ElementDecoration.cpp b/Source/Core/ElementDecoration.cpp index 1098904f3..3e4b8ad07 100644 --- a/Source/Core/ElementDecoration.cpp +++ b/Source/Core/ElementDecoration.cpp @@ -88,7 +88,8 @@ bool ElementDecoration::ReloadDecorators() } } - const auto& decorator_list = style_sheet->InstanceDecorators(*decorators_ptr, source); + Vector> decorator_list; + style_sheet->InstanceDecorators(decorator_list, *decorators_ptr, source); for (const SharedPtr& decorator : decorator_list) { diff --git a/Source/Core/PropertyParserDecorator.cpp b/Source/Core/PropertyParserDecorator.cpp index 31a6f85c0..7b0ffe4c2 100644 --- a/Source/Core/PropertyParserDecorator.cpp +++ b/Source/Core/PropertyParserDecorator.cpp @@ -65,6 +65,7 @@ bool PropertyParserDecorator::ParseValue(Property& property, const String& decor StringList decorator_string_list; StringUtilities::ExpandString(decorator_string_list, decorator_string_value, ',', '(', ')'); + decorators.caching = true; decorators.value = decorator_string_value; decorators.list.reserve(decorator_string_list.size()); diff --git a/Source/Core/StyleSheet.cpp b/Source/Core/StyleSheet.cpp index 9bfa117a1..fee0c91ff 100644 --- a/Source/Core/StyleSheet.cpp +++ b/Source/Core/StyleSheet.cpp @@ -112,22 +112,26 @@ const Keyframes * StyleSheet::GetKeyframes(const String & name) const return nullptr; } -const Vector>& StyleSheet::InstanceDecorators(const DecoratorDeclarationList& declaration_list, const PropertySource* source) const +void StyleSheet::InstanceDecorators(Vector>& decorator_list, const DecoratorDeclarationList& declaration_list, const PropertySource* source) const { // Generate the cache key. Relative paths of textures may be affected by the source path, and ultimately // which texture should be displayed. Thus, we need to include this path in the cache key. - String key; - key.reserve(declaration_list.value.size() + 1 + (source ? source->path.size() : 0)); - key = declaration_list.value; - key += ';'; - if (source) - key += source->path; - - auto it_cache = decorator_cache.find(key); - if (it_cache != decorator_cache.end()) - return it_cache->second; - - Vector>& decorators = decorator_cache[key]; + String cache_key; + if (declaration_list.caching) + { + cache_key.reserve(declaration_list.value.size() + 1 + (source ? source->path.size() : 0)); + cache_key = declaration_list.value; + cache_key += ';'; + if (source) + cache_key += source->path; + + auto it_cache = decorator_cache.find(cache_key); + if (it_cache != decorator_cache.end()) + { + decorator_list = it_cache->second; + return; + } + } for (const DecoratorDeclaration& declaration : declaration_list.list) { @@ -136,7 +140,7 @@ const Vector>& StyleSheet::InstanceDecorators(const D RMLUI_ZoneScopedN("InstanceDecorator"); if (SharedPtr decorator = declaration.instancer->InstanceDecorator(declaration.type, declaration.properties, DecoratorInstancerInterface(*this, source))) - decorators.push_back(std::move(decorator)); + decorator_list.push_back(std::move(decorator)); else Log::Message(Log::LT_WARNING, "Decorator '%s' in '%s' could not be instanced, declared at %s:%d", declaration.type.c_str(), declaration_list.value.c_str(), source ? source->path.c_str() : "", source ? source->line_number : -1); } @@ -149,13 +153,16 @@ const Vector>& StyleSheet::InstanceDecorators(const D decorator = it_map->second.decorator; if (decorator) - decorators.push_back(std::move(decorator)); + decorator_list.push_back(std::move(decorator)); else Log::Message(Log::LT_WARNING, "Decorator name '%s' could not be found in any @decorator rule, declared at %s:%d", declaration.type.c_str(), source ? source->path.c_str() : "", source ? source->line_number : -1); } } - return decorators; + if (declaration_list.caching) + { + decorator_cache[cache_key].insert(decorator_cache[cache_key].end(), decorator_list.begin(), decorator_list.end()); + } } const Sprite* StyleSheet::GetSprite(const String& name) const From de68fc880979e28c4e6cb513e2d84eedc8353e59 Mon Sep 17 00:00:00 2001 From: suddenly Date: Tue, 28 Feb 2023 13:59:18 +0500 Subject: [PATCH 03/11] Added support for @decorator rule --- Include/RmlUi/Core/StyleSheet.h | 3 ++ Source/Core/ElementAnimation.cpp | 58 +++++++++++++++++++++++++------- Source/Core/StyleSheet.cpp | 10 +++++- 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/Include/RmlUi/Core/StyleSheet.h b/Include/RmlUi/Core/StyleSheet.h index dc95563c2..665735173 100644 --- a/Include/RmlUi/Core/StyleSheet.h +++ b/Include/RmlUi/Core/StyleSheet.h @@ -66,6 +66,9 @@ class RMLUICORE_API StyleSheet final : public NonCopyMoveable /// Builds the node index for a combined style sheet. void BuildNodeIndex(); + /// Returns the DecoratorSpecification of the given name, or null if it does not exist. + const DecoratorSpecification* GetDecoratorSpecification(const String& name) const; + /// Returns the Keyframes of the given name, or null if it does not exist. /// @lifetime The returned pointer becomes invalidated whenever the style sheet is re-generated. Do not store this pointer or references to subobjects around. const Keyframes* GetKeyframes(const String& name) const; diff --git a/Source/Core/ElementAnimation.cpp b/Source/Core/ElementAnimation.cpp index 26abb7726..9ad73665b 100644 --- a/Source/Core/ElementAnimation.cpp +++ b/Source/Core/ElementAnimation.cpp @@ -170,6 +170,10 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f auto& d0 = p0.value.GetReference(); auto& d1 = p1.value.GetReference(); + const StyleSheet* style_sheet = element.GetStyleSheet(); + if (!style_sheet) + return Property{ d0, Property::DECORATOR }; + if (d0->list.size() != d1->list.size()) { RMLUI_ERRORMSG("The number of decorators should be the same"); @@ -182,28 +186,56 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f for (int i = 0; i < d0->list.size(); i++) { const DecoratorDeclaration& declaration1 = d0->list[i]; const DecoratorDeclaration& declaration2 = d1->list[i]; - - if (declaration1.type != declaration2.type) + DecoratorDeclaration new_declaration; + + PropertyMap properties1; + if (!declaration1.instancer) + { + const DecoratorSpecification* decorator_specification = style_sheet->GetDecoratorSpecification(declaration1.type); + if (!decorator_specification) + { + RMLUI_ERRORMSG("@decorator rule not found"); + return Property{ d0, Property::DECORATOR }; + } + + properties1 = decorator_specification->properties.GetProperties(); + + new_declaration.type = decorator_specification->decorator_type; + new_declaration.instancer = Factory::GetDecoratorInstancer(new_declaration.type); + } + else { - RMLUI_ERRORMSG("The types of decorators should be the same"); - return Property{ d0, Property::DECORATOR }; + properties1 = declaration1.properties.GetProperties(); + + new_declaration.instancer = declaration1.instancer; + new_declaration.type = declaration1.type; } - if (declaration1.instancer != declaration2.instancer) + new_declaration.instancer->GetPropertySpecification().SetPropertyDefaults(new_declaration.properties); + + PropertyMap properties2; + if (!declaration2.instancer) { - RMLUI_ERRORMSG("The instancers of decorators should be the same"); - return Property{ d0, Property::DECORATOR }; + const DecoratorSpecification* decorator_specification = style_sheet->GetDecoratorSpecification(declaration2.type); + if (!decorator_specification) + { + RMLUI_ERRORMSG("@decorator rule not found"); + return Property{ d0, Property::DECORATOR }; + } + + properties2 = decorator_specification->properties.GetProperties(); + } + else + { + properties2 = declaration2.properties.GetProperties(); } - PropertyDictionary result_roperties; - declaration1.instancer->GetPropertySpecification().SetPropertyDefaults(result_roperties); - const PropertyMap& properties1 = declaration1.properties.GetProperties(); - const PropertyMap& properties2 = declaration2.properties.GetProperties(); for (auto it1 = properties1.begin(), it2 = properties2.begin(); it1 != properties1.end() || it2 != properties2.end(); it1++, it2++) { - result_roperties.SetProperty(it1->first, InterpolateProperties(it1->second, it2->second, alpha, element, it1->second.definition)); + new_declaration.properties.SetProperty(it1->first, InterpolateProperties(it1->second, it2->second, alpha, element, it1->second.definition)); } - declaration_list->list.push_back(DecoratorDeclaration{ declaration1.type, declaration1.instancer, result_roperties }); + + declaration_list->list.push_back(std::move(new_declaration)); } return Property{ DecoratorsPtr(std::move(declaration_list)), Property::DECORATOR }; diff --git a/Source/Core/StyleSheet.cpp b/Source/Core/StyleSheet.cpp index fee0c91ff..2607824e8 100644 --- a/Source/Core/StyleSheet.cpp +++ b/Source/Core/StyleSheet.cpp @@ -103,8 +103,16 @@ void StyleSheet::BuildNodeIndex() root->BuildIndex(styled_node_index); } +const DecoratorSpecification* StyleSheet::GetDecoratorSpecification(const String& name) const +{ + auto it = decorator_map.find(name); + if (it != decorator_map.end()) + return &(it->second); + return nullptr; +} + // Returns the Keyframes of the given name, or null if it does not exist. -const Keyframes * StyleSheet::GetKeyframes(const String & name) const +const Keyframes* StyleSheet::GetKeyframes(const String & name) const { auto it = keyframes.find(name); if (it != keyframes.end()) From 8ca542334a7088f3e66a5f53efa05785e0622fd3 Mon Sep 17 00:00:00 2001 From: suddenly Date: Tue, 28 Feb 2023 14:02:42 +0500 Subject: [PATCH 04/11] int -> size_t --- Source/Core/ElementAnimation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/ElementAnimation.cpp b/Source/Core/ElementAnimation.cpp index 9ad73665b..e75e2b304 100644 --- a/Source/Core/ElementAnimation.cpp +++ b/Source/Core/ElementAnimation.cpp @@ -183,7 +183,7 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f UniquePtr declaration_list(new DecoratorDeclarationList); declaration_list->caching = false; - for (int i = 0; i < d0->list.size(); i++) { + for (size_t i = 0; i < d0->list.size(); i++) { const DecoratorDeclaration& declaration1 = d0->list[i]; const DecoratorDeclaration& declaration2 = d1->list[i]; DecoratorDeclaration new_declaration; From 4c9e41f86369e7d656a187a8d3d12d6938ba894b Mon Sep 17 00:00:00 2001 From: suddenly Date: Tue, 28 Feb 2023 23:51:10 +0500 Subject: [PATCH 05/11] Added decorator rule support for existing implementation --- Source/Core/ElementAnimation.cpp | 138 +++++++++++++++++---------- Tests/Source/UnitTests/Animation.cpp | 128 +++++++++++++++++++++++++ 2 files changed, 217 insertions(+), 49 deletions(-) create mode 100644 Tests/Source/UnitTests/Animation.cpp diff --git a/Source/Core/ElementAnimation.cpp b/Source/Core/ElementAnimation.cpp index e75e2b304..16909fe1c 100644 --- a/Source/Core/ElementAnimation.cpp +++ b/Source/Core/ElementAnimation.cpp @@ -167,78 +167,118 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f if (p0.unit == Property::DECORATOR && p1.unit == Property::DECORATOR) { - auto& d0 = p0.value.GetReference(); - auto& d1 = p1.value.GetReference(); + auto DiscreteInterpolation = [&]() { return alpha < 0.5f ? p0 : p1; }; + auto PrepareDeclaration = [&](const DecoratorDeclaration& declaration) { + if (declaration.instancer) + return declaration; + + // Maybe it needs to be taken out of the lambda? + const StyleSheet* style_sheet = element.GetStyleSheet(); + if (!style_sheet) + { + RMLUI_ERRORMSG("style_sheet is empty."); + return declaration; + } + + const DecoratorSpecification* specification = style_sheet->GetDecoratorSpecification(declaration.type); + if (!specification) + { + RMLUI_ERRORMSG("Invalid DecoratorSpecification pointer."); + return declaration; + } - const StyleSheet* style_sheet = element.GetStyleSheet(); - if (!style_sheet) - return Property{ d0, Property::DECORATOR }; + return DecoratorDeclaration{ specification->decorator_type, Factory::GetDecoratorInstancer(specification->decorator_type), + specification->properties, declaration.paint_area }; + }; - if (d0->list.size() != d1->list.size()) + auto& ptr0 = p0.value.GetReference(); + auto& ptr1 = p1.value.GetReference(); + if (!ptr0 || !ptr1) { - RMLUI_ERRORMSG("The number of decorators should be the same"); - return Property{ d0, Property::DECORATOR }; + RMLUI_ERRORMSG("Invalid decorator pointer, were the decorator keys properly prepared?"); + return DiscreteInterpolation(); } - UniquePtr declaration_list(new DecoratorDeclarationList); - declaration_list->caching = false; + const bool p0_smaller = (ptr0->list.size() < ptr1->list.size()); + auto& small = (p0_smaller ? ptr0->list : ptr1->list); + auto& big = (p0_smaller ? ptr1->list : ptr0->list); - for (size_t i = 0; i < d0->list.size(); i++) { - const DecoratorDeclaration& declaration1 = d0->list[i]; - const DecoratorDeclaration& declaration2 = d1->list[i]; - DecoratorDeclaration new_declaration; + // Build the new, interpolated decorator. + UniquePtr decorator(new DecoratorDeclarationList); + decorator->list.reserve(ptr0->list.size()); - PropertyMap properties1; - if (!declaration1.instancer) + // Interpolate decorators that have common types. + for (size_t i = 0; i < small.size(); i++) + { + const DecoratorDeclaration& d0 = PrepareDeclaration(ptr0->list[i]); + const DecoratorDeclaration& d1 = PrepareDeclaration(ptr1->list[i]); + if(!d0.instancer || !d1.instancer) { - const DecoratorSpecification* decorator_specification = style_sheet->GetDecoratorSpecification(declaration1.type); - if (!decorator_specification) - { - RMLUI_ERRORMSG("@decorator rule not found"); - return Property{ d0, Property::DECORATOR }; - } - - properties1 = decorator_specification->properties.GetProperties(); - - new_declaration.type = decorator_specification->decorator_type; - new_declaration.instancer = Factory::GetDecoratorInstancer(new_declaration.type); + return DiscreteInterpolation(); } - else - { - properties1 = declaration1.properties.GetProperties(); - new_declaration.instancer = declaration1.instancer; - new_declaration.type = declaration1.type; + if (d0.instancer != d1.instancer || d0.paint_area != d1.paint_area || d0.type != d1.type || + d0.properties.GetNumProperties() != d1.properties.GetNumProperties()) + { + // Incompatible decorators, fall back to discrete interpolation. + return DiscreteInterpolation(); } - new_declaration.instancer->GetPropertySpecification().SetPropertyDefaults(new_declaration.properties); + decorator->list.push_back(DecoratorDeclaration{d0.type, d0.instancer, PropertyDictionary(), d0.paint_area}); + PropertyDictionary& props = decorator->list.back().properties; - PropertyMap properties2; - if (!declaration2.instancer) + const auto& props0 = d0.properties.GetProperties(); + const auto& props1 = d1.properties.GetProperties(); + + for (const auto& pair0 : props0) { - const DecoratorSpecification* decorator_specification = style_sheet->GetDecoratorSpecification(declaration2.type); - if (!decorator_specification) + const PropertyId id = pair0.first; + const Property& prop0 = pair0.second; + + auto it = props1.find(id); + if (it == props1.end()) { - RMLUI_ERRORMSG("@decorator rule not found"); - return Property{ d0, Property::DECORATOR }; + RMLUI_ERRORMSG("Incompatible decorator properties."); + return DiscreteInterpolation(); } + const Property& prop1 = it->second; - properties2 = decorator_specification->properties.GetProperties(); - } - else - { - properties2 = declaration2.properties.GetProperties(); + Property p = InterpolateProperties(prop0, prop1, alpha, element, prop0.definition); + p.definition = prop0.definition; + props.SetProperty(id, p); } + } - for (auto it1 = properties1.begin(), it2 = properties2.begin(); it1 != properties1.end() || it2 != properties2.end(); it1++, it2++) + // Append any trailing decorators from the largest list and interpolate against the default values of its type. + for (size_t i = small.size(); i < big.size(); i++) + { + const DecoratorDeclaration& d_big = PrepareDeclaration(big[i]); + + decorator->list.push_back(DecoratorDeclaration{ d_big.type, d_big.instancer, PropertyDictionary(), d_big.paint_area }); + DecoratorDeclaration& d_new = decorator->list.back(); + + const PropertySpecification& specification = d_new.instancer->GetPropertySpecification(); + + const PropertyMap& props_big = d_big.properties.GetProperties(); + for (const auto& pair_big : props_big) { - new_declaration.properties.SetProperty(it1->first, InterpolateProperties(it1->second, it2->second, alpha, element, it1->second.definition)); + const PropertyId id = pair_big.first; + const PropertyDefinition* underlying_definition = specification.GetProperty(id); + if (!underlying_definition) + return DiscreteInterpolation(); + + const Property& p_big = pair_big.second; + const Property& p_small = *underlying_definition->GetDefaultValue(); + const Property& p_interp0 = (p0_smaller ? p_small : p_big); + const Property& p_interp1 = (p0_smaller ? p_big : p_small); + + Property p = InterpolateProperties(p_interp0, p_interp1, alpha, element, p_big.definition); + p.definition = p_big.definition; + d_new.properties.SetProperty(id, p); } - - declaration_list->list.push_back(std::move(new_declaration)); } - return Property{ DecoratorsPtr(std::move(declaration_list)), Property::DECORATOR }; + return Property{ DecoratorsPtr(std::move(decorator)), Unit::DECORATOR }; } // Fall back to discrete interpolation for incompatible units. diff --git a/Tests/Source/UnitTests/Animation.cpp b/Tests/Source/UnitTests/Animation.cpp new file mode 100644 index 000000000..b1d4d08cd --- /dev/null +++ b/Tests/Source/UnitTests/Animation.cpp @@ -0,0 +1,128 @@ +/* + * This source file is part of RmlUi, the HTML/CSS Interface Middleware + * + * For the latest information, see http://github.com/mikke89/RmlUi + * + * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd + * Copyright (c) 2019 The RmlUi Team, and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "../Common/TestsInterface.h" +#include "../Common/TestsShell.h" +#include +#include +#include +#include + +using namespace Rml; + +static const String document_decorator_rml = R"( + + + Test + + + + + +
+ + +)"; + +TEST_CASE("animation.decorator") +{ + struct Test { + String from; + String to; + String expected_25p; // expected interpolated value at 25% progression + }; + + Vector tests{ + { + "gradient(horizontal transparent transparent)", + "gradient(horizontal white white)", + "gradient(horizontal rgba(127,127,127,63) rgba(127,127,127,63))", + }, + { + "none", + "gradient(horizontal transparent transparent)", + "gradient(horizontal rgba(220,220,220,191) rgba(220,220,220,191))", + }, + { + "none", + "gradient(horizontal transparent transparent), gradient(vertical transparent transparent)", + "gradient(horizontal rgba(220,220,220,191) rgba(220,220,220,191)), gradient(horizontal rgba(220,220,220,191) rgba(220,220,220,191))", + }, + { + "gradient(horizontal white white), gradient(vertical transparent transparent)", + "gradient(horizontal transparent transparent)", + "gradient(horizontal rgba(220,220,220,191) rgba(220,220,220,191)), gradient(vertical rgba(127,127,127,63) rgba(127,127,127,63))", + } + }; + + TestsSystemInterface* system_interface = TestsShell::GetTestsSystemInterface(); + Context* context = TestsShell::GetContext(); + context->SetDensityIndependentPixelRatio(2.0f); + + for (const Test& test : tests) + { + const double t_final = 0.1; + + system_interface->SetTime(0.0); + String document_rml = + Rml::CreateString(document_decorator_rml.size() + 512, document_decorator_rml.c_str(), test.from.c_str(), test.to.c_str()); + + ElementDocument* document = context->LoadDocumentFromMemory(document_rml, "assets/"); + Element* element = document->GetChild(0); + + document->Show(); + TestsShell::RenderLoop(); + + system_interface->SetTime(0.25 * t_final); + TestsShell::RenderLoop(); + CHECK_MESSAGE(element->GetProperty("decorator") == test.expected_25p, "from: ", test.from, ", to: ", test.to); + + document->Close(); + } + + system_interface->SetTime(0.0); + + TestsShell::ShutdownShell(); +} From 31a525c8c8506d47f162e2aa6329e1bd7beb503c Mon Sep 17 00:00:00 2001 From: suddenly Date: Wed, 1 Mar 2023 00:04:55 +0500 Subject: [PATCH 06/11] Fixed compilation. --- Include/RmlUi/Core/StyleSheet.h | 4 +- Include/RmlUi/Core/StyleSheetTypes.h | 1 - Source/Core/ElementAnimation.cpp | 11 ++-- Source/Core/ElementDecoration.cpp | 3 +- Source/Core/PropertyParserDecorator.cpp | 4 +- Source/Core/StyleSheet.cpp | 69 +++++++++++++++---------- 6 files changed, 53 insertions(+), 39 deletions(-) diff --git a/Include/RmlUi/Core/StyleSheet.h b/Include/RmlUi/Core/StyleSheet.h index 665735173..daf04e091 100644 --- a/Include/RmlUi/Core/StyleSheet.h +++ b/Include/RmlUi/Core/StyleSheet.h @@ -46,6 +46,8 @@ class StyleSheetParser; struct PropertySource; struct Sprite; +using DecoratorPtrList = Vector>; + /** StyleSheet maintains a single stylesheet definition. A stylesheet can be combined with another stylesheet to create a new, merged stylesheet. @@ -81,7 +83,7 @@ class RMLUICORE_API StyleSheet final : public NonCopyMoveable SharedPtr GetElementDefinition(const Element* element) const; /// Returns a list of instanced decorators from the declarations. The instances are cached for faster future retrieval. - void InstanceDecorators(Vector>& decorator_list, const DecoratorDeclarationList& declaration_list, const PropertySource* decorator_source) const; + const DecoratorPtrList& InstanceDecorators(const DecoratorDeclarationList& declaration_list, const PropertySource* decorator_source) const; private: StyleSheet(); diff --git a/Include/RmlUi/Core/StyleSheetTypes.h b/Include/RmlUi/Core/StyleSheetTypes.h index 7da3e1cbf..fde4dba84 100644 --- a/Include/RmlUi/Core/StyleSheetTypes.h +++ b/Include/RmlUi/Core/StyleSheetTypes.h @@ -66,7 +66,6 @@ struct DecoratorDeclaration { struct DecoratorDeclarationList { Vector list; String value; - bool caching; }; struct MediaBlock { diff --git a/Source/Core/ElementAnimation.cpp b/Source/Core/ElementAnimation.cpp index 16909fe1c..719b47939 100644 --- a/Source/Core/ElementAnimation.cpp +++ b/Source/Core/ElementAnimation.cpp @@ -187,8 +187,7 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f return declaration; } - return DecoratorDeclaration{ specification->decorator_type, Factory::GetDecoratorInstancer(specification->decorator_type), - specification->properties, declaration.paint_area }; + return DecoratorDeclaration{ specification->decorator_type, Factory::GetDecoratorInstancer(specification->decorator_type), specification->properties }; }; auto& ptr0 = p0.value.GetReference(); @@ -217,14 +216,14 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f return DiscreteInterpolation(); } - if (d0.instancer != d1.instancer || d0.paint_area != d1.paint_area || d0.type != d1.type || + if (d0.instancer != d1.instancer || d0.type != d1.type || d0.properties.GetNumProperties() != d1.properties.GetNumProperties()) { // Incompatible decorators, fall back to discrete interpolation. return DiscreteInterpolation(); } - decorator->list.push_back(DecoratorDeclaration{d0.type, d0.instancer, PropertyDictionary(), d0.paint_area}); + decorator->list.push_back(DecoratorDeclaration{ d0.type, d0.instancer, PropertyDictionary() }); PropertyDictionary& props = decorator->list.back().properties; const auto& props0 = d0.properties.GetProperties(); @@ -254,7 +253,7 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f { const DecoratorDeclaration& d_big = PrepareDeclaration(big[i]); - decorator->list.push_back(DecoratorDeclaration{ d_big.type, d_big.instancer, PropertyDictionary(), d_big.paint_area }); + decorator->list.push_back(DecoratorDeclaration{ d_big.type, d_big.instancer, PropertyDictionary() }); DecoratorDeclaration& d_new = decorator->list.back(); const PropertySpecification& specification = d_new.instancer->GetPropertySpecification(); @@ -278,7 +277,7 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f } } - return Property{ DecoratorsPtr(std::move(decorator)), Unit::DECORATOR }; + return Property{ DecoratorsPtr(std::move(decorator)), Property::DECORATOR }; } // Fall back to discrete interpolation for incompatible units. diff --git a/Source/Core/ElementDecoration.cpp b/Source/Core/ElementDecoration.cpp index 3e4b8ad07..03057c2a0 100644 --- a/Source/Core/ElementDecoration.cpp +++ b/Source/Core/ElementDecoration.cpp @@ -88,8 +88,7 @@ bool ElementDecoration::ReloadDecorators() } } - Vector> decorator_list; - style_sheet->InstanceDecorators(decorator_list, *decorators_ptr, source); + const DecoratorPtrList& decorator_list = style_sheet->InstanceDecorators(*decorators_ptr, source); for (const SharedPtr& decorator : decorator_list) { diff --git a/Source/Core/PropertyParserDecorator.cpp b/Source/Core/PropertyParserDecorator.cpp index 7b0ffe4c2..18a00349a 100644 --- a/Source/Core/PropertyParserDecorator.cpp +++ b/Source/Core/PropertyParserDecorator.cpp @@ -59,13 +59,11 @@ bool PropertyParserDecorator::ParseValue(Property& property, const String& decor RMLUI_ZoneScoped; - DecoratorDeclarationList decorators; - // Make sure we don't split inside the parenthesis since they may appear in decorator shorthands. StringList decorator_string_list; StringUtilities::ExpandString(decorator_string_list, decorator_string_value, ',', '(', ')'); - decorators.caching = true; + DecoratorDeclarationList decorators; decorators.value = decorator_string_value; decorators.list.reserve(decorator_string_list.size()); diff --git a/Source/Core/StyleSheet.cpp b/Source/Core/StyleSheet.cpp index 2607824e8..ba248ae28 100644 --- a/Source/Core/StyleSheet.cpp +++ b/Source/Core/StyleSheet.cpp @@ -120,57 +120,74 @@ const Keyframes* StyleSheet::GetKeyframes(const String & name) const return nullptr; } -void StyleSheet::InstanceDecorators(Vector>& decorator_list, const DecoratorDeclarationList& declaration_list, const PropertySource* source) const +const DecoratorPtrList& StyleSheet::InstanceDecorators(const DecoratorDeclarationList& declaration_list, const PropertySource* source) const { + RMLUI_ASSERT_NONRECURSIVE; // Since we may return a reference to the below static variable. + static DecoratorPtrList non_cached_decorator_list; + + // Empty declaration values are used for interpolated values which we don't want to cache. + const bool enable_cache = !declaration_list.value.empty(); + // Generate the cache key. Relative paths of textures may be affected by the source path, and ultimately // which texture should be displayed. Thus, we need to include this path in the cache key. - String cache_key; - if (declaration_list.caching) + String key; + + if (enable_cache) { - cache_key.reserve(declaration_list.value.size() + 1 + (source ? source->path.size() : 0)); - cache_key = declaration_list.value; - cache_key += ';'; + key.reserve(declaration_list.value.size() + 1 + (source ? source->path.size() : 0)); + key = declaration_list.value; + key += ';'; if (source) - cache_key += source->path; + key += source->path; - auto it_cache = decorator_cache.find(cache_key); + auto it_cache = decorator_cache.find(key); if (it_cache != decorator_cache.end()) - { - decorator_list = it_cache->second; - return; - } + return it_cache->second; + } + else + { + non_cached_decorator_list.clear(); } + DecoratorPtrList& decorators = enable_cache ? decorator_cache[key] : non_cached_decorator_list; + decorators.reserve(declaration_list.list.size()); + for (const DecoratorDeclaration& declaration : declaration_list.list) { + SharedPtr decorator; + if (declaration.instancer) { RMLUI_ZoneScopedN("InstanceDecorator"); - - if (SharedPtr decorator = declaration.instancer->InstanceDecorator(declaration.type, declaration.properties, DecoratorInstancerInterface(*this, source))) - decorator_list.push_back(std::move(decorator)); - else - Log::Message(Log::LT_WARNING, "Decorator '%s' in '%s' could not be instanced, declared at %s:%d", declaration.type.c_str(), declaration_list.value.c_str(), source ? source->path.c_str() : "", source ? source->line_number : -1); + decorator = + declaration.instancer->InstanceDecorator(declaration.type, declaration.properties, DecoratorInstancerInterface(*this, source)); + + if (!decorator) + Log::Message(Log::LT_WARNING, "Decorator '%s' in '%s' could not be instanced, declared at %s:%d", declaration.type.c_str(), + declaration_list.value.c_str(), source ? source->path.c_str() : "", source ? source->line_number : -1); } else { // If we have no instancer, this means the type is the name of an @decorator rule. - SharedPtr decorator; auto it_map = decorator_map.find(declaration.type); if (it_map != decorator_map.end()) decorator = it_map->second.decorator; - if (decorator) - decorator_list.push_back(std::move(decorator)); - else - Log::Message(Log::LT_WARNING, "Decorator name '%s' could not be found in any @decorator rule, declared at %s:%d", declaration.type.c_str(), source ? source->path.c_str() : "", source ? source->line_number : -1); + if (!decorator) + Log::Message(Log::LT_WARNING, "Decorator name '%s' could not be found in any @decorator rule, declared at %s:%d", + declaration.type.c_str(), source ? source->path.c_str() : "", source ? source->line_number : -1); } - } - if (declaration_list.caching) - { - decorator_cache[cache_key].insert(decorator_cache[cache_key].end(), decorator_list.begin(), decorator_list.end()); + if (!decorator) + { + decorators.clear(); + break; + } + + decorators.push_back(std::move(decorator)); } + + return decorators; } const Sprite* StyleSheet::GetSprite(const String& name) const From 045705537df851b02fa879fa07eddb91835d9974 Mon Sep 17 00:00:00 2001 From: suddenly Date: Fri, 3 Mar 2023 02:25:00 +0500 Subject: [PATCH 07/11] Final code migration and adding tests --- Include/RmlUi/Core/PropertySpecification.h | 2 +- Source/Core/ElementAnimation.cpp | 20 +++- Source/Core/PropertyParserDecorator.cpp | 4 +- Source/Core/PropertySpecification.cpp | 28 ++++- Source/Core/TypeConverter.cpp | 18 +++- Tests/Source/Common/TestsInterface.cpp | 7 +- Tests/Source/Common/TestsInterface.h | 4 + Tests/Source/Common/TestsShell.cpp | 5 + Tests/Source/Common/TestsShell.h | 4 + Tests/Source/UnitTests/Animation.cpp | 113 +++++++++++++++++++-- 10 files changed, 183 insertions(+), 22 deletions(-) diff --git a/Include/RmlUi/Core/PropertySpecification.h b/Include/RmlUi/Core/PropertySpecification.h index 39195a57b..175967453 100644 --- a/Include/RmlUi/Core/PropertySpecification.h +++ b/Include/RmlUi/Core/PropertySpecification.h @@ -120,7 +120,7 @@ class RMLUICORE_API PropertySpecification void SetPropertyDefaults(PropertyDictionary& dictionary) const; /// Returns the properties of dictionary converted to a string. - String PropertiesToString(const PropertyDictionary& dictionary) const; + String PropertiesToString(const PropertyDictionary& dictionary, bool include_name, char delimiter) const; private: using Properties = Vector< UniquePtr >; diff --git a/Source/Core/ElementAnimation.cpp b/Source/Core/ElementAnimation.cpp index 719b47939..d1f26fb63 100644 --- a/Source/Core/ElementAnimation.cpp +++ b/Source/Core/ElementAnimation.cpp @@ -27,13 +27,16 @@ */ #include "ElementAnimation.h" -#include "ElementStyle.h" -#include "TransformUtilities.h" +#include "../../Include/RmlUi/Core/DecoratorInstancer.h" #include "../../Include/RmlUi/Core/Element.h" #include "../../Include/RmlUi/Core/PropertyDefinition.h" +#include "../../Include/RmlUi/Core/PropertySpecification.h" #include "../../Include/RmlUi/Core/StyleSheetSpecification.h" +#include "../../Include/RmlUi/Core/StyleSheetTypes.h" #include "../../Include/RmlUi/Core/Transform.h" #include "../../Include/RmlUi/Core/TransformPrimitive.h" +#include "ElementStyle.h" +#include "TransformUtilities.h" namespace Rml { @@ -507,6 +510,15 @@ static bool PrepareTransforms(Vector& keys, Element& element, int return (count_iterations < max_iterations); } +static void PrepareDecorator(AnimationKey& key) +{ + Property& property = key.property; + RMLUI_ASSERT(property.value.GetType() == Variant::DECORATORSPTR); + + if (!property.value.GetReference()) + property.value = MakeShared(); +} + ElementAnimation::ElementAnimation(PropertyId property_id, ElementAnimationOrigin origin, const Property& current_value, Element& element, double start_world_time, float duration, int num_iterations, bool alternate_direction) : @@ -539,6 +551,10 @@ bool ElementAnimation::InternalAddKey(float time, const Property& in_property, E { result = PrepareTransforms(keys, element, (int)keys.size() - 1); } + else if (keys.back().property.unit == Property::DECORATOR) + { + PrepareDecorator(keys.back()); + } if (!result) { diff --git a/Source/Core/PropertyParserDecorator.cpp b/Source/Core/PropertyParserDecorator.cpp index 18a00349a..a1423b463 100644 --- a/Source/Core/PropertyParserDecorator.cpp +++ b/Source/Core/PropertyParserDecorator.cpp @@ -52,8 +52,8 @@ bool PropertyParserDecorator::ParseValue(Property& property, const String& decor if (decorator_string_value.empty() || decorator_string_value == "none") { - property.value = Variant(); - property.unit = Property::UNKNOWN; + property.value = Variant(DecoratorsPtr()); + property.unit = Property::DECORATOR; return true; } diff --git a/Source/Core/PropertySpecification.cpp b/Source/Core/PropertySpecification.cpp index b040abfc4..5545b0fe7 100644 --- a/Source/Core/PropertySpecification.cpp +++ b/Source/Core/PropertySpecification.cpp @@ -29,11 +29,12 @@ #include "../../Include/RmlUi/Core/PropertySpecification.h" #include "../../Include/RmlUi/Core/Debug.h" #include "../../Include/RmlUi/Core/Log.h" +#include "../../Include/RmlUi/Core/Profiling.h" #include "../../Include/RmlUi/Core/PropertyDefinition.h" #include "../../Include/RmlUi/Core/PropertyDictionary.h" -#include "../../Include/RmlUi/Core/Profiling.h" -#include "PropertyShorthandDefinition.h" #include "IdNameMap.h" +#include "PropertyShorthandDefinition.h" +#include #include #include @@ -432,13 +433,30 @@ void PropertySpecification::SetPropertyDefaults(PropertyDictionary& dictionary) } } -String PropertySpecification::PropertiesToString(const PropertyDictionary& dictionary) const +String PropertySpecification::PropertiesToString(const PropertyDictionary& dictionary, bool include_name, char delimiter) const { + const PropertyMap& properties = dictionary.GetProperties(); + + // For determinism we print the strings in order of increasing property ids. + Vector ids; + ids.reserve(properties.size()); + for (auto& pair : properties) + ids.push_back(pair.first); + + std::sort(ids.begin(), ids.end()); + String result; - for (auto& pair : dictionary.GetProperties()) + for (PropertyId id : ids) { - result += property_map->GetName(pair.first) + ": " + pair.second.ToString() + '\n'; + const Property& p = properties.find(id)->second; + if (include_name) + result += property_map->GetName(id) + ": "; + result += p.ToString() + delimiter; } + + if (!result.empty()) + result.pop_back(); + return result; } diff --git a/Source/Core/TypeConverter.cpp b/Source/Core/TypeConverter.cpp index cfed64ccc..f47bad3c5 100644 --- a/Source/Core/TypeConverter.cpp +++ b/Source/Core/TypeConverter.cpp @@ -31,6 +31,7 @@ #include "../../Include/RmlUi/Core/StyleSheetTypes.h" #include "../../Include/RmlUi/Core/Animation.h" #include "../../Include/RmlUi/Core/Transform.h" +#include "../../Include/RmlUi/Core/PropertySpecification.h" #include "../../Include/RmlUi/Core/TransformPrimitive.h" #include "../../Include/RmlUi/Core/PropertyDictionary.h" #include "TransformUtilities.h" @@ -127,8 +128,23 @@ bool TypeConverter::Convert(const DecoratorsPtr& src, Str { if (!src || src->list.empty()) dest = "none"; - else + else if (!src->value.empty()) dest += src->value; + else + { + dest.clear(); + for (const DecoratorDeclaration& declaration : src->list) + { + dest += declaration.type; + if (auto instancer = declaration.instancer) + { + dest += '(' + instancer->GetPropertySpecification().PropertiesToString(declaration.properties, false, ' ') + ')'; + } + dest += ", "; + } + if (dest.size() > 2) + dest.resize(dest.size() - 2); + } return true; } diff --git a/Tests/Source/Common/TestsInterface.cpp b/Tests/Source/Common/TestsInterface.cpp index 198aaa63e..7668c16eb 100644 --- a/Tests/Source/Common/TestsInterface.cpp +++ b/Tests/Source/Common/TestsInterface.cpp @@ -33,7 +33,7 @@ double TestsSystemInterface::GetElapsedTime() { - return 0.0; + return elapsed_time; } bool TestsSystemInterface::LogMessage(Rml::Log::Type type, const Rml::String& message) @@ -78,6 +78,11 @@ void TestsSystemInterface::SetNumExpectedWarnings(int in_num_expected_warnings) num_expected_warnings = in_num_expected_warnings; } +void TestsSystemInterface::SetTime(double t) +{ + elapsed_time = t; +} + void TestsRenderInterface::RenderGeometry(Rml::Vertex* /*vertices*/, int /*num_vertices*/, int* /*indices*/, int /*num_indices*/, const Rml::TextureHandle /*texture*/, const Rml::Vector2f& /*translation*/) { counters.render_calls += 1; diff --git a/Tests/Source/Common/TestsInterface.h b/Tests/Source/Common/TestsInterface.h index c810529e3..6ad8c4609 100644 --- a/Tests/Source/Common/TestsInterface.h +++ b/Tests/Source/Common/TestsInterface.h @@ -43,7 +43,11 @@ class TestsSystemInterface : public Rml::SystemInterface { // warnings and errors until the next call. void SetNumExpectedWarnings(int num_expected_warnings); + void SetTime(double t); + private: + double elapsed_time = 0.0; + int num_logged_warnings = 0; int num_expected_warnings = 0; diff --git a/Tests/Source/Common/TestsShell.cpp b/Tests/Source/Common/TestsShell.cpp index 6df733618..8fa7f81bd 100644 --- a/Tests/Source/Common/TestsShell.cpp +++ b/Tests/Source/Common/TestsShell.cpp @@ -226,3 +226,8 @@ TestsRenderInterface* TestsShell::GetTestsRenderInterface() return &shell_render_interface; #endif } + +TestsSystemInterface* TestsShell::GetTestsSystemInterface() +{ + return &tests_system_interface; +} \ No newline at end of file diff --git a/Tests/Source/Common/TestsShell.h b/Tests/Source/Common/TestsShell.h index c6a665669..c610ff003 100644 --- a/Tests/Source/Common/TestsShell.h +++ b/Tests/Source/Common/TestsShell.h @@ -32,6 +32,7 @@ #include namespace Rml { class RenderInterface; } class TestsRenderInterface; +class TestsSystemInterface; namespace TestsShell { @@ -52,11 +53,14 @@ namespace TestsShell { // or until 'ShutdownShell()'. void SetNumExpectedWarnings(int num_warnings); + void SetTime(double t); + // Stats only available for the dummy renderer. Rml::String GetRenderStats(); // Returns nullptr if the dummy renderer is not being used. TestsRenderInterface* GetTestsRenderInterface(); + TestsSystemInterface* GetTestsSystemInterface(); } #endif diff --git a/Tests/Source/UnitTests/Animation.cpp b/Tests/Source/UnitTests/Animation.cpp index b1d4d08cd..07da8862d 100644 --- a/Tests/Source/UnitTests/Animation.cpp +++ b/Tests/Source/UnitTests/Animation.cpp @@ -47,6 +47,10 @@ static const String document_decorator_rml = R"( right: 0; bottom: 0; } + + @decorator from_rule : gradient { %s } + @decorator to_rule: gradient{ %s } + @keyframes mix { from { decorator: %s; } to { decorator: %s; } @@ -69,32 +73,121 @@ static const String document_decorator_rml = R"( TEST_CASE("animation.decorator") { struct Test { + String from_rule; + String to_rule; String from; String to; String expected_25p; // expected interpolated value at 25% progression }; Vector tests{ + // Only standard declaration { + "", "", + "gradient(horizontal transparent transparent)", "gradient(horizontal white white)", - "gradient(horizontal rgba(127,127,127,63) rgba(127,127,127,63))", + + "gradient(horizontal rgba(255,255,255,63) rgba(255,255,255,63))", }, { + "", "", + "none", "gradient(horizontal transparent transparent)", - "gradient(horizontal rgba(220,220,220,191) rgba(220,220,220,191))", + + "gradient(horizontal rgba(255,255,255,191) rgba(255,255,255,191))", }, { + "", "", + "none", "gradient(horizontal transparent transparent), gradient(vertical transparent transparent)", - "gradient(horizontal rgba(220,220,220,191) rgba(220,220,220,191)), gradient(horizontal rgba(220,220,220,191) rgba(220,220,220,191))", + + "gradient(horizontal rgba(255,255,255,191) rgba(255,255,255,191)), gradient(horizontal rgba(255,255,255,191) rgba(255,255,255,191))", }, { - "gradient(horizontal white white), gradient(vertical transparent transparent)", - "gradient(horizontal transparent transparent)", - "gradient(horizontal rgba(220,220,220,191) rgba(220,220,220,191)), gradient(vertical rgba(127,127,127,63) rgba(127,127,127,63))", - } + "", "", + + "gradient(horizontal transparent transparent), gradient(vertical transparent transparent)", + "none", + + "gradient(horizontal rgba(255,255,255,63) rgba(255,255,255,63)), gradient(vertical rgba(255,255,255,63) rgba(255,255,255,63))", + }, + + /// Only rule declaration + { + "direction: horizontal; start-color: transparent; stop-color: transparent;", + "direction: horizontal; start-color: white; stop-color: white;", + + "from_rule", + "to_rule", + + "gradient(horizontal rgba(255,255,255,63) rgba(255,255,255,63))", + }, + { + "", + "direction: horizontal; start-color: transparent; stop-color: transparent;", + + "from_rule", + "to_rule", + + "gradient(horizontal rgba(255,255,255,191) rgba(255,255,255,191))", + }, + { + "direction: vertical; start-color: transparent; stop-color: transparent;", + "", + + "from_rule", + "to_rule", + + "gradient(vertical rgba(255,255,255,63) rgba(255,255,255,63))", + }, + + /// Mix rule and standard declaration + { + "direction: horizontal; start-color: transparent; stop-color: transparent;", + "", + + "from_rule", + "gradient(horizontal white white)", + + "gradient(horizontal rgba(255,255,255,63) rgba(255,255,255,63))", + }, + { + "", + "direction: horizontal; start-color: transparent; stop-color: transparent;", + + "none", + "to_rule", + + "gradient(horizontal rgba(255,255,255,191) rgba(255,255,255,191))", + }, + { + "direction: vertical; start-color: transparent; stop-color: transparent;", + "", + + "from_rule", + "none", + + "gradient(vertical rgba(255,255,255,63) rgba(255,255,255,63))", + }, + { + "", "", + + "from_rule, to_rule", + "gradient(horizontal transparent transparent), gradient(vertical transparent transparent)", + + "gradient(horizontal rgba(255,255,255,191) rgba(255,255,255,191)), gradient(horizontal rgba(255,255,255,191) rgba(255,255,255,191))", + }, + { + "", "", + + "gradient(horizontal transparent transparent), gradient(vertical transparent transparent)", + "from_rule, to_rule", + + "gradient(horizontal rgba(255,255,255,63) rgba(255,255,255,63)), gradient(vertical rgba(255,255,255,63) rgba(255,255,255,63))", + }, }; TestsSystemInterface* system_interface = TestsShell::GetTestsSystemInterface(); @@ -106,8 +199,8 @@ TEST_CASE("animation.decorator") const double t_final = 0.1; system_interface->SetTime(0.0); - String document_rml = - Rml::CreateString(document_decorator_rml.size() + 512, document_decorator_rml.c_str(), test.from.c_str(), test.to.c_str()); + String document_rml = Rml::CreateString(document_decorator_rml.size() + 512, document_decorator_rml.c_str(), test.from_rule.c_str(), + test.to_rule.c_str(), test.from.c_str(), test.to.c_str()); ElementDocument* document = context->LoadDocumentFromMemory(document_rml, "assets/"); Element* element = document->GetChild(0); @@ -125,4 +218,4 @@ TEST_CASE("animation.decorator") system_interface->SetTime(0.0); TestsShell::ShutdownShell(); -} +} \ No newline at end of file From cc961e1e87da62b5ec0b841e02977f30024abd9f Mon Sep 17 00:00:00 2001 From: suddenly Date: Fri, 3 Mar 2023 02:38:15 +0500 Subject: [PATCH 08/11] Fixed linux/windows building --- Source/Core/ElementAnimation.cpp | 1 + Source/Core/TypeConverter.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/Source/Core/ElementAnimation.cpp b/Source/Core/ElementAnimation.cpp index d1f26fb63..dfa0e0c84 100644 --- a/Source/Core/ElementAnimation.cpp +++ b/Source/Core/ElementAnimation.cpp @@ -28,6 +28,7 @@ #include "ElementAnimation.h" #include "../../Include/RmlUi/Core/DecoratorInstancer.h" +#include "../../Include/RmlUi/Core/Factory.h" #include "../../Include/RmlUi/Core/Element.h" #include "../../Include/RmlUi/Core/PropertyDefinition.h" #include "../../Include/RmlUi/Core/PropertySpecification.h" diff --git a/Source/Core/TypeConverter.cpp b/Source/Core/TypeConverter.cpp index f47bad3c5..6f9580ea1 100644 --- a/Source/Core/TypeConverter.cpp +++ b/Source/Core/TypeConverter.cpp @@ -30,6 +30,7 @@ #include "../../Include/RmlUi/Core/StyleSheetSpecification.h" #include "../../Include/RmlUi/Core/StyleSheetTypes.h" #include "../../Include/RmlUi/Core/Animation.h" +#include "../../Include/RmlUi/Core/DecoratorInstancer.h" #include "../../Include/RmlUi/Core/Transform.h" #include "../../Include/RmlUi/Core/PropertySpecification.h" #include "../../Include/RmlUi/Core/TransformPrimitive.h" From 30040ebfc44a39138e3273c023b4fcc88ec90e71 Mon Sep 17 00:00:00 2001 From: suddenly Date: Fri, 3 Mar 2023 02:42:14 +0500 Subject: [PATCH 09/11] And Anotheone another one fix for build --- Source/Core/ElementAnimation.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Core/ElementAnimation.cpp b/Source/Core/ElementAnimation.cpp index dfa0e0c84..a001cf2b9 100644 --- a/Source/Core/ElementAnimation.cpp +++ b/Source/Core/ElementAnimation.cpp @@ -34,6 +34,7 @@ #include "../../Include/RmlUi/Core/PropertySpecification.h" #include "../../Include/RmlUi/Core/StyleSheetSpecification.h" #include "../../Include/RmlUi/Core/StyleSheetTypes.h" +#include "../../Include/RmlUi/Core/StyleSheet.h" #include "../../Include/RmlUi/Core/Transform.h" #include "../../Include/RmlUi/Core/TransformPrimitive.h" #include "ElementStyle.h" From c4fc0eb787fe0889ec686eec32017b2b37d58e70 Mon Sep 17 00:00:00 2001 From: suddenly Date: Sun, 5 Mar 2023 00:29:45 +0500 Subject: [PATCH 10/11] Added co-author, comments and slightly redesigned getting decorator rule Co-Authored-By: Michael R. P. Ragazzon --- Source/Core/ElementAnimation.cpp | 80 +++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/Source/Core/ElementAnimation.cpp b/Source/Core/ElementAnimation.cpp index a001cf2b9..b61a10cd5 100644 --- a/Source/Core/ElementAnimation.cpp +++ b/Source/Core/ElementAnimation.cpp @@ -173,26 +173,22 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f if (p0.unit == Property::DECORATOR && p1.unit == Property::DECORATOR) { auto DiscreteInterpolation = [&]() { return alpha < 0.5f ? p0 : p1; }; - auto PrepareDeclaration = [&](const DecoratorDeclaration& declaration) { - if (declaration.instancer) - return declaration; - - // Maybe it needs to be taken out of the lambda? + auto FindDecoratorSpecification = [&](const String* type) -> const DecoratorSpecification* { const StyleSheet* style_sheet = element.GetStyleSheet(); if (!style_sheet) { - RMLUI_ERRORMSG("style_sheet is empty."); - return declaration; + Log::Message(Log::LT_WARNING, "Failed to get element stylesheet for '%s' decorator rule.", type->c_str()); + return nullptr; } - const DecoratorSpecification* specification = style_sheet->GetDecoratorSpecification(declaration.type); + const DecoratorSpecification* specification = style_sheet->GetDecoratorSpecification(*type); if (!specification) { - RMLUI_ERRORMSG("Invalid DecoratorSpecification pointer."); - return declaration; + Log::Message(Log::LT_WARNING, "Could not find DecoratorSpecification for '%s' decorator rule.", type->c_str()); + return nullptr; } - return DecoratorDeclaration{ specification->decorator_type, Factory::GetDecoratorInstancer(specification->decorator_type), specification->properties }; + return specification; }; auto& ptr0 = p0.value.GetReference(); @@ -214,25 +210,50 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f // Interpolate decorators that have common types. for (size_t i = 0; i < small.size(); i++) { - const DecoratorDeclaration& d0 = PrepareDeclaration(ptr0->list[i]); - const DecoratorDeclaration& d1 = PrepareDeclaration(ptr1->list[i]); - if(!d0.instancer || !d1.instancer) + const String* d0_type = &ptr0->list[i].type; + DecoratorInstancer* d0_instancer = ptr0->list[i].instancer; + const PropertyDictionary* d0_properties = &ptr0->list[i].properties; + + const String* d1_type = &ptr1->list[i].type; + DecoratorInstancer* d1_instancer = ptr1->list[i].instancer; + const PropertyDictionary* d1_properties = &ptr1->list[i].properties; + + // If d0 is decorator rule, we take its type, instancer and properties from the found rule + if (!d0_instancer) { - return DiscreteInterpolation(); + const DecoratorSpecification* d0_spec = FindDecoratorSpecification(d0_type); + if (!d0_spec) + return DiscreteInterpolation(); + + d0_type = &d0_spec->decorator_type; + d0_instancer = Factory::GetDecoratorInstancer(d0_spec->decorator_type); + d0_properties = &d0_spec->properties; + } + + // We repeat the fix of d1 fields + if (!d1_instancer) + { + const DecoratorSpecification* d1_spec = FindDecoratorSpecification(d1_type); + if (!d1_spec) + return DiscreteInterpolation(); + + d1_type = &d1_spec->decorator_type; + d1_instancer = Factory::GetDecoratorInstancer(d1_spec->decorator_type); + d1_properties = &d1_spec->properties; } - if (d0.instancer != d1.instancer || d0.type != d1.type || - d0.properties.GetNumProperties() != d1.properties.GetNumProperties()) + if (d0_instancer != d1_instancer || *d0_type != *d1_type || + d0_properties->GetNumProperties() != d1_properties->GetNumProperties()) { // Incompatible decorators, fall back to discrete interpolation. return DiscreteInterpolation(); } - decorator->list.push_back(DecoratorDeclaration{ d0.type, d0.instancer, PropertyDictionary() }); + decorator->list.push_back(DecoratorDeclaration{ *d0_type, d0_instancer, PropertyDictionary() }); PropertyDictionary& props = decorator->list.back().properties; - const auto& props0 = d0.properties.GetProperties(); - const auto& props1 = d1.properties.GetProperties(); + const auto& props0 = d0_properties->GetProperties(); + const auto& props1 = d1_properties->GetProperties(); for (const auto& pair0 : props0) { @@ -256,14 +277,27 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f // Append any trailing decorators from the largest list and interpolate against the default values of its type. for (size_t i = small.size(); i < big.size(); i++) { - const DecoratorDeclaration& d_big = PrepareDeclaration(big[i]); + const String* dbig_type = &big[i].type; + DecoratorInstancer* dbig_instancer = big[i].instancer; + const PropertyDictionary* dbig_properties = &big[i].properties; + + if (!dbig_instancer) + { + const DecoratorSpecification* dbig_spec = FindDecoratorSpecification(dbig_type); + if (!dbig_spec) + return DiscreteInterpolation(); + + dbig_type = &dbig_spec->decorator_type; + dbig_instancer = Factory::GetDecoratorInstancer(dbig_spec->decorator_type); + dbig_properties = &dbig_spec->properties; + } - decorator->list.push_back(DecoratorDeclaration{ d_big.type, d_big.instancer, PropertyDictionary() }); + decorator->list.push_back(DecoratorDeclaration{ *dbig_type, dbig_instancer, PropertyDictionary() }); DecoratorDeclaration& d_new = decorator->list.back(); const PropertySpecification& specification = d_new.instancer->GetPropertySpecification(); - const PropertyMap& props_big = d_big.properties.GetProperties(); + const PropertyMap& props_big = dbig_properties->GetProperties(); for (const auto& pair_big : props_big) { const PropertyId id = pair_big.first; From 0c6042f53e6eccb77ca58ebfb2a409099a7d7911 Mon Sep 17 00:00:00 2001 From: suddenly Date: Mon, 6 Mar 2023 21:34:38 +0500 Subject: [PATCH 11/11] Added DecoratorDeclarationView --- Include/RmlUi/Core/StyleSheetTypes.h | 11 ++++ Source/Core/ElementAnimation.cpp | 83 +++++++++------------------- 2 files changed, 37 insertions(+), 57 deletions(-) diff --git a/Include/RmlUi/Core/StyleSheetTypes.h b/Include/RmlUi/Core/StyleSheetTypes.h index fde4dba84..e3701056d 100644 --- a/Include/RmlUi/Core/StyleSheetTypes.h +++ b/Include/RmlUi/Core/StyleSheetTypes.h @@ -30,6 +30,7 @@ #define RMLUI_CORE_STYLESHEETTYPES_H #include "PropertyDictionary.h" +#include "Factory.h" #include "Types.h" #include "Utilities.h" @@ -63,6 +64,16 @@ struct DecoratorDeclaration { DecoratorInstancer* instancer; PropertyDictionary properties; }; + +struct DecoratorDeclarationView { + DecoratorDeclarationView(const DecoratorDeclaration& declaration) : type(declaration.type), instancer(declaration.instancer), properties(declaration.properties) {} + DecoratorDeclarationView(const DecoratorSpecification* specification) : type(specification->decorator_type), instancer(Factory::GetDecoratorInstancer(specification->decorator_type)), properties(specification->properties) {} + + const String& type; + DecoratorInstancer* instancer; + const PropertyDictionary& properties; +}; + struct DecoratorDeclarationList { Vector list; String value; diff --git a/Source/Core/ElementAnimation.cpp b/Source/Core/ElementAnimation.cpp index b61a10cd5..f3a463f63 100644 --- a/Source/Core/ElementAnimation.cpp +++ b/Source/Core/ElementAnimation.cpp @@ -173,22 +173,27 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f if (p0.unit == Property::DECORATOR && p1.unit == Property::DECORATOR) { auto DiscreteInterpolation = [&]() { return alpha < 0.5f ? p0 : p1; }; - auto FindDecoratorSpecification = [&](const String* type) -> const DecoratorSpecification* { + + // We construct DecoratorDeclarationView from declaration if it has instancer, otherwise we look for DecoratorSpecification and return DecoratorDeclarationView from it + auto GetDecoratorDeclarationView = [&](const DecoratorDeclaration& declaration) -> DecoratorDeclarationView { + if (declaration.instancer) + return DecoratorDeclarationView{ declaration }; + const StyleSheet* style_sheet = element.GetStyleSheet(); if (!style_sheet) { - Log::Message(Log::LT_WARNING, "Failed to get element stylesheet for '%s' decorator rule.", type->c_str()); - return nullptr; + Log::Message(Log::LT_WARNING, "Failed to get element stylesheet for '%s' decorator rule.", declaration.type.c_str()); + return DecoratorDeclarationView{ declaration }; } - const DecoratorSpecification* specification = style_sheet->GetDecoratorSpecification(*type); + const DecoratorSpecification* specification = style_sheet->GetDecoratorSpecification(declaration.type); if (!specification) { - Log::Message(Log::LT_WARNING, "Could not find DecoratorSpecification for '%s' decorator rule.", type->c_str()); - return nullptr; + Log::Message(Log::LT_WARNING, "Could not find DecoratorSpecification for '%s' decorator rule.", declaration.type.c_str()); + return DecoratorDeclarationView{ declaration }; } - return specification; + return DecoratorDeclarationView{ specification }; }; auto& ptr0 = p0.value.GetReference(); @@ -210,50 +215,24 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f // Interpolate decorators that have common types. for (size_t i = 0; i < small.size(); i++) { - const String* d0_type = &ptr0->list[i].type; - DecoratorInstancer* d0_instancer = ptr0->list[i].instancer; - const PropertyDictionary* d0_properties = &ptr0->list[i].properties; - - const String* d1_type = &ptr1->list[i].type; - DecoratorInstancer* d1_instancer = ptr1->list[i].instancer; - const PropertyDictionary* d1_properties = &ptr1->list[i].properties; - - // If d0 is decorator rule, we take its type, instancer and properties from the found rule - if (!d0_instancer) - { - const DecoratorSpecification* d0_spec = FindDecoratorSpecification(d0_type); - if (!d0_spec) - return DiscreteInterpolation(); + DecoratorDeclarationView d0_view{ GetDecoratorDeclarationView(ptr0->list[i]) }; + DecoratorDeclarationView d1_view{ GetDecoratorDeclarationView(ptr1->list[i]) }; - d0_type = &d0_spec->decorator_type; - d0_instancer = Factory::GetDecoratorInstancer(d0_spec->decorator_type); - d0_properties = &d0_spec->properties; - } - - // We repeat the fix of d1 fields - if (!d1_instancer) - { - const DecoratorSpecification* d1_spec = FindDecoratorSpecification(d1_type); - if (!d1_spec) - return DiscreteInterpolation(); - - d1_type = &d1_spec->decorator_type; - d1_instancer = Factory::GetDecoratorInstancer(d1_spec->decorator_type); - d1_properties = &d1_spec->properties; - } + if (!d0_view.instancer || !d1_view.instancer) + return DiscreteInterpolation(); - if (d0_instancer != d1_instancer || *d0_type != *d1_type || - d0_properties->GetNumProperties() != d1_properties->GetNumProperties()) + if (d0_view.instancer != d1_view.instancer || d0_view.type != d1_view.type || + d0_view.properties.GetNumProperties() != d1_view.properties.GetNumProperties()) { // Incompatible decorators, fall back to discrete interpolation. return DiscreteInterpolation(); } - decorator->list.push_back(DecoratorDeclaration{ *d0_type, d0_instancer, PropertyDictionary() }); + decorator->list.push_back(DecoratorDeclaration{ d0_view.type, d0_view.instancer, PropertyDictionary() }); PropertyDictionary& props = decorator->list.back().properties; - const auto& props0 = d0_properties->GetProperties(); - const auto& props1 = d1_properties->GetProperties(); + const auto& props0 = d0_view.properties.GetProperties(); + const auto& props1 = d1_view.properties.GetProperties(); for (const auto& pair0 : props0) { @@ -277,27 +256,17 @@ static Property InterpolateProperties(const Property & p0, const Property& p1, f // Append any trailing decorators from the largest list and interpolate against the default values of its type. for (size_t i = small.size(); i < big.size(); i++) { - const String* dbig_type = &big[i].type; - DecoratorInstancer* dbig_instancer = big[i].instancer; - const PropertyDictionary* dbig_properties = &big[i].properties; - - if (!dbig_instancer) - { - const DecoratorSpecification* dbig_spec = FindDecoratorSpecification(dbig_type); - if (!dbig_spec) - return DiscreteInterpolation(); + DecoratorDeclarationView dbig_view{ GetDecoratorDeclarationView(big[i]) }; - dbig_type = &dbig_spec->decorator_type; - dbig_instancer = Factory::GetDecoratorInstancer(dbig_spec->decorator_type); - dbig_properties = &dbig_spec->properties; - } + if (!dbig_view.instancer) + return DiscreteInterpolation(); - decorator->list.push_back(DecoratorDeclaration{ *dbig_type, dbig_instancer, PropertyDictionary() }); + decorator->list.push_back(DecoratorDeclaration{ dbig_view.type, dbig_view.instancer, PropertyDictionary() }); DecoratorDeclaration& d_new = decorator->list.back(); const PropertySpecification& specification = d_new.instancer->GetPropertySpecification(); - const PropertyMap& props_big = dbig_properties->GetProperties(); + const PropertyMap& props_big = dbig_view.properties.GetProperties(); for (const auto& pair_big : props_big) { const PropertyId id = pair_big.first;