Skip to content

Commit

Permalink
Animation support for decorators
Browse files Browse the repository at this point in the history
  • Loading branch information
mikke89 committed Jul 22, 2022
1 parent d464be9 commit 8023d31
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 27 deletions.
2 changes: 1 addition & 1 deletion Include/RmlUi/Core/PropertySpecification.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<PropertyDefinition> >;
Expand Down
10 changes: 10 additions & 0 deletions Samples/basic/effect/data/effect.rml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@
.hue_rotate { filter: hue-rotate(260deg); }
.invert { filter: invert(100%); }

@keyframes animate-filter {
from { filter: drop-shadow(#f00), opacity(1.0), sepia(1.0); }
to { filter: drop-shadow(#000 30px 20px 5px), opacity(0.2), sepia(0.2); }
}
.animate {
animation: animate-filter 1.5s cubic-in-out infinite alternate;
}

.shader { decorator: shader("creation"); }

.opacity_low { filter: opacity(0.2); }
Expand Down Expand Up @@ -176,6 +184,8 @@

<div class="box hue_rotate"><img sprite="icon-invader"/>Hello, do you feel the funk?</div>
<div class="box big hue_rotate"><img src="/assets/rmlui.tga"/>Hello, do you feel the funk?</div>

<div class="box animate"><img sprite="icon-invader"/>Hello, do you feel the funk?</div>
<div class="box saturate"><img sprite="icon-invader"/>Hello, do you feel the funk?</div>
<div class="box invert"><img sprite="icon-invader"/>Hello, do you feel the funk?</div>
<div class="box blur"><img sprite="icon-invader"/>Hello, do you feel the funk?</div>
Expand Down
129 changes: 122 additions & 7 deletions Source/Core/ElementAnimation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -165,6 +168,97 @@ static Property InterpolateProperties(const Property& p0, const Property& p1, fl
return Property{ TransformPtr(std::move(t)), Unit::TRANSFORM };
}

if (p0.unit == Unit::DECORATOR && p1.unit == Unit::DECORATOR)
{
auto DiscreteInterpolation = [&]() { return alpha < 0.5f ? p0 : p1; };

auto& ptr0 = p0.value.GetReference<DecoratorsPtr>();
auto& ptr1 = p1.value.GetReference<DecoratorsPtr>();
if (!ptr0 || !ptr1)
{
RMLUI_ERRORMSG("Invalid decorator pointer, were the decorator keys properly prepared?");
return DiscreteInterpolation();
}

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);

// Build the new, interpolated decorator.
UniquePtr<DecoratorDeclarationList> decorator(new DecoratorDeclarationList);
decorator->list.reserve(ptr0->list.size());

// Interpolate decorators that have common types.
for (size_t i = 0; i < small.size(); i++)
{
const DecoratorDeclaration& d0 = ptr0->list[i];
const DecoratorDeclaration& d1 = ptr1->list[i];

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();
}

decorator->list.push_back(DecoratorDeclaration{d0.type, d0.instancer, PropertyDictionary(), d0.paint_area});
PropertyDictionary& props = decorator->list.back().properties;

const auto& props0 = d0.properties.GetProperties();
const auto& props1 = d1.properties.GetProperties();

for (const auto& pair0 : props0)
{
const PropertyId id = pair0.first;
const Property& prop0 = pair0.second;

auto it = props1.find(id);
if (it == props1.end())
{
RMLUI_ERRORMSG("Incompatible decorator properties.");
return DiscreteInterpolation();
}
const Property& prop1 = it->second;

Property p = InterpolateProperties(prop0, prop1, alpha, element, prop0.definition);
p.definition = prop0.definition;
props.SetProperty(id, p);
}
}

// 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 = big[i];
RMLUI_ASSERT(d_big.instancer);

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)
{
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);
}
}

return Property{DecoratorsPtr(std::move(decorator)), Unit::DECORATOR};
}

// Fall back to discrete interpolation for incompatible units.
return alpha < 0.5f ? p0 : p1;
}
Expand Down Expand Up @@ -247,13 +341,9 @@ static PrepareTransformResult PrepareTransformPair(Transform& t0, Transform& t1,
if (TransformUtilities::TryConvertToMatchingGenericType(small[i_small], big[i_big]))
{
// They matched exactly or in their more generic form. One or both primitives may have been converted.
match_success = true;
if (big[i_big].type != big_type)
changed_big = true;
}

if (match_success)
{
matching_indices.push_back(i_big);
match_success = true;
i_big += 1;
Expand Down Expand Up @@ -392,6 +482,27 @@ static bool PrepareTransforms(Vector<AnimationKey>& keys, Element& element, int
return (count_iterations < max_iterations);
}

static bool PrepareDecorator(AnimationKey& key)
{
Property& property = key.property;
RMLUI_ASSERT(property.value.GetType() == Variant::DECORATORSPTR);

if (!property.value.GetReference<DecoratorsPtr>())
property.value = MakeShared<DecoratorDeclarationList>();

const DecoratorDeclarationList& declaration_list = *property.value.GetReference<DecoratorsPtr>();

for (const DecoratorDeclaration& declaration : declaration_list.list)
{
if (!declaration.instancer)
{
Log::Message(Log::LT_WARNING, "Animation keys cannot include decorator values containing @decorator names.");
return false;
}
}

return true;
}

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)
: property_id(property_id), duration(duration), num_iterations(num_iterations), alternate_direction(alternate_direction), last_update_world_time(start_world_time),
Expand All @@ -407,7 +518,7 @@ ElementAnimation::ElementAnimation(PropertyId property_id, ElementAnimationOrigi

bool ElementAnimation::InternalAddKey(float time, const Property& in_property, Element& element, Tween tween)
{
const Units valid_units = (Unit::NUMBER_LENGTH_PERCENT | Unit::ANGLE | Unit::COLOUR | Unit::TRANSFORM | Unit::KEYWORD);
const Units valid_units = (Unit::NUMBER_LENGTH_PERCENT | Unit::ANGLE | Unit::COLOUR | Unit::TRANSFORM | Unit::KEYWORD | Unit::DECORATOR);

if (!Any(in_property.unit & valid_units))
{
Expand All @@ -422,6 +533,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 == Unit::DECORATOR)
{
result = PrepareDecorator(keys.back());
}

if (!result)
{
Expand Down
4 changes: 2 additions & 2 deletions Source/Core/PropertyParserDecorator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ bool PropertyParserDecorator::ParseValue(Property& property, const String& decor

if (decorator_string_value.empty() || decorator_string_value == "none")
{
property.value = Variant();
property.unit = Unit::UNKNOWN;
property.value = Variant(DecoratorsPtr());
property.unit = Unit::DECORATOR;
return true;
}

Expand Down
29 changes: 23 additions & 6 deletions Source/Core/PropertySpecification.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <algorithm>
#include <limits.h>
#include <stdint.h>

Expand Down Expand Up @@ -452,17 +453,33 @@ 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<PropertyId> 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;
}


bool PropertySpecification::ParsePropertyValues(StringList& values_list, const String& values, bool split_values) const
{
String value;
Expand Down
32 changes: 23 additions & 9 deletions Source/Core/StyleSheet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,34 @@ const Keyframes * StyleSheet::GetKeyframes(const String & name) 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 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;
if (enable_cache)
{
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;
}
else
{
non_cached_decorator_list.clear();
}

DecoratorPtrList& decorators = decorator_cache[key];
DecoratorPtrList& decorators = enable_cache ? decorator_cache[key] : non_cached_decorator_list;
decorators.reserve(declaration_list.list.size());

for (const DecoratorDeclaration& declaration : declaration_list.list)
Expand Down
20 changes: 18 additions & 2 deletions Source/Core/TypeConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
#include "../../Include/RmlUi/Core/TypeConverter.h"
#include "../../Include/RmlUi/Core/Animation.h"
#include "../../Include/RmlUi/Core/DecorationTypes.h"
#include "../../Include/RmlUi/Core/DecoratorInstancer.h"
#include "../../Include/RmlUi/Core/PropertyDictionary.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"
Expand Down Expand Up @@ -154,13 +156,27 @@ bool TypeConverter<DecoratorsPtr, DecoratorsPtr>::Convert(const DecoratorsPtr& s
return true;
}


bool TypeConverter<DecoratorsPtr, String>::Convert(const DecoratorsPtr& src, String& dest)
{
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;
}

Expand Down

0 comments on commit 8023d31

Please sign in to comment.