Xxxx transitions with base virtual animation

Diffs=
f9b1e8ec2 Xxxx transitions with base virtual animation (#7157)

Co-authored-by: hernan <hernan@rive.app>
diff --git a/.rive_head b/.rive_head
index a74cdfb..853c915 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-e5db5a652fedb8dd8c9708f02536e23ab12210d9
+f9b1e8ec2ff7960990f87ef855c669f6a4a86b1d
diff --git a/include/rive/animation/animation_reset.hpp b/include/rive/animation/animation_reset.hpp
new file mode 100644
index 0000000..6be0c99
--- /dev/null
+++ b/include/rive/animation/animation_reset.hpp
@@ -0,0 +1,32 @@
+#ifndef _RIVE_ANIMATION_RESET_HPP_
+#define _RIVE_ANIMATION_RESET_HPP_
+
+#include <string>
+#include "rive/artboard.hpp"
+#include <rive/animation/animation_reset.hpp>
+#include "rive/core/binary_writer.hpp"
+#include "rive/core/vector_binary_writer.hpp"
+#include "rive/core/binary_data_reader.hpp"
+
+namespace rive
+{
+
+class AnimationReset
+{
+private:
+    VectorBinaryWriter m_binaryWriter;
+    BinaryDataReader m_binaryReader;
+    std::vector<uint8_t> m_WriteBuffer;
+
+public:
+    AnimationReset();
+    void writeObjectId(uint32_t objectId);
+    void writeTotalProperties(uint8_t value);
+    void writePropertyKey(uint8_t value);
+    void writePropertyValue(float value);
+    void apply(Artboard* artboard);
+    void complete();
+    void clear();
+};
+} // namespace rive
+#endif
\ No newline at end of file
diff --git a/include/rive/animation/animation_reset_factory.hpp b/include/rive/animation/animation_reset_factory.hpp
new file mode 100644
index 0000000..8a4db56
--- /dev/null
+++ b/include/rive/animation/animation_reset_factory.hpp
@@ -0,0 +1,40 @@
+#ifndef _RIVE_ANIMATION_RESET_FACTORY_HPP_
+#define _RIVE_ANIMATION_RESET_FACTORY_HPP_
+
+#include <string>
+#include <mutex>
+#include "rive/animation/animation_reset.hpp"
+#include "rive/animation/state_instance.hpp"
+#include "rive/animation/linear_animation.hpp"
+#include "rive/artboard.hpp"
+
+namespace rive
+{
+
+class AnimationResetFactory
+{
+    static std::vector<std::unique_ptr<AnimationReset>> m_resources;
+    static std::mutex m_mutex;
+
+private:
+    static void fromState(StateInstance* stateInstance,
+                          std::vector<const LinearAnimation*>& animations);
+
+public:
+    static std::unique_ptr<AnimationReset> getInstance();
+    static std::unique_ptr<AnimationReset> fromStates(StateInstance* stateFrom,
+                                                      StateInstance* currentState,
+                                                      ArtboardInstance* artboard);
+    static std::unique_ptr<AnimationReset> fromAnimations(
+        std::vector<const LinearAnimation*>& animations,
+        ArtboardInstance* artboard,
+        bool useFirstAsBaseline);
+    static void release(std::unique_ptr<AnimationReset> value);
+#ifdef TESTING
+    // Used in testing to check pooled resources;
+    static int resourcesCount() { return m_resources.size(); };
+    static void releaseResources() { m_resources.clear(); };
+#endif
+};
+} // namespace rive
+#endif
\ No newline at end of file
diff --git a/include/rive/animation/animation_state_instance.hpp b/include/rive/animation/animation_state_instance.hpp
index 0eb15cc..7e73343 100644
--- a/include/rive/animation/animation_state_instance.hpp
+++ b/include/rive/animation/animation_state_instance.hpp
@@ -20,7 +20,7 @@
     AnimationStateInstance(const AnimationState* animationState, ArtboardInstance* instance);
 
     void advance(float seconds, StateMachineInstance* stateMachineInstance) override;
-    void apply(float mix) override;
+    void apply(ArtboardInstance* instance, float mix) override;
 
     bool keepGoing() const override;
     void clearSpilledTime() override;
diff --git a/include/rive/animation/blend_state_1d_instance.hpp b/include/rive/animation/blend_state_1d_instance.hpp
index d0c81a2..ed1df09 100644
--- a/include/rive/animation/blend_state_1d_instance.hpp
+++ b/include/rive/animation/blend_state_1d_instance.hpp
@@ -4,6 +4,8 @@
 #include "rive/animation/blend_state_instance.hpp"
 #include "rive/animation/blend_state_1d.hpp"
 #include "rive/animation/blend_animation_1d.hpp"
+#include "rive/animation/animation_reset.hpp"
+#include "rive/animation/animation_reset_factory.hpp"
 
 namespace rive
 {
@@ -12,11 +14,14 @@
 private:
     BlendStateAnimationInstance<BlendAnimation1D>* m_From = nullptr;
     BlendStateAnimationInstance<BlendAnimation1D>* m_To = nullptr;
+    std::unique_ptr<AnimationReset> m_AnimationReset;
     int animationIndex(float value);
 
 public:
     BlendState1DInstance(const BlendState1D* blendState, ArtboardInstance* instance);
+    ~BlendState1DInstance();
     void advance(float seconds, StateMachineInstance* stateMachineInstance) override;
+    void apply(ArtboardInstance* instance, float mix) override;
 };
 } // namespace rive
 #endif
\ No newline at end of file
diff --git a/include/rive/animation/blend_state_instance.hpp b/include/rive/animation/blend_state_instance.hpp
index fb52f12..bf34466 100644
--- a/include/rive/animation/blend_state_instance.hpp
+++ b/include/rive/animation/blend_state_instance.hpp
@@ -5,8 +5,11 @@
 #include <vector>
 #include "rive/animation/state_instance.hpp"
 #include "rive/animation/blend_state.hpp"
+#include "rive/animation/layer_state_flags.hpp"
 #include "rive/animation/linear_animation_instance.hpp"
 #include "rive/animation/state_machine_instance.hpp"
+#include "rive/animation/animation_reset.hpp"
+#include "rive/animation/animation_reset_factory.hpp"
 
 namespace rive
 {
@@ -49,6 +52,15 @@
             m_AnimationInstances.emplace_back(
                 BlendStateAnimationInstance<T>(static_cast<T*>(blendAnimation), instance));
         }
+        if ((static_cast<LayerStateFlags>(blendState->flags()) & LayerStateFlags::Reset) ==
+            LayerStateFlags::Reset)
+        {
+            auto animations = std::vector<const LinearAnimation*>();
+            for (auto blendAnimation : blendState->animations())
+            {
+                animations.push_back(blendAnimation->animation());
+            }
+        }
     }
 
     bool keepGoing() const override { return m_KeepGoing; }
@@ -71,7 +83,7 @@
         }
     }
 
-    void apply(float mix) override
+    void apply(ArtboardInstance* instance, float mix) override
     {
         for (auto& animation : m_AnimationInstances)
         {
diff --git a/include/rive/animation/keyed_object.hpp b/include/rive/animation/keyed_object.hpp
index 860e11f..3aba7b9 100644
--- a/include/rive/animation/keyed_object.hpp
+++ b/include/rive/animation/keyed_object.hpp
@@ -24,6 +24,20 @@
 
     StatusCode import(ImportStack& importStack) override;
 
+    const KeyedProperty* getProperty(size_t index) const
+    {
+        if (index < m_keyedProperties.size())
+        {
+            return m_keyedProperties[index].get();
+        }
+        else
+        {
+            return nullptr;
+        }
+    }
+
+    size_t numKeyedProperties() const { return m_keyedProperties.size(); }
+
 private:
     std::vector<std::unique_ptr<KeyedProperty>> m_keyedProperties;
 };
diff --git a/include/rive/animation/keyed_property.hpp b/include/rive/animation/keyed_property.hpp
index 1ef73ce..d9d0425 100644
--- a/include/rive/animation/keyed_property.hpp
+++ b/include/rive/animation/keyed_property.hpp
@@ -26,6 +26,14 @@
     void apply(Core* object, float time, float mix);
 
     StatusCode import(ImportStack& importStack) override;
+    KeyFrame* first() const
+    {
+        if (m_keyFrames.size() > 0)
+        {
+            return m_keyFrames.front().get();
+        }
+        return nullptr;
+    }
 
 private:
     int closestFrameIndex(float seconds, int exactOffset = 0) const;
diff --git a/include/rive/animation/layer_state_flags.hpp b/include/rive/animation/layer_state_flags.hpp
index 880bd35..c9d6134 100644
--- a/include/rive/animation/layer_state_flags.hpp
+++ b/include/rive/animation/layer_state_flags.hpp
@@ -12,6 +12,8 @@
     /// Whether the transition is disabled.
     Random = 1 << 0,
 
+    /// Whether the blend should include an instance to reset values on apply
+    Reset = 1 << 1,
 };
 
 inline constexpr LayerStateFlags operator&(LayerStateFlags lhs, LayerStateFlags rhs)
diff --git a/include/rive/animation/linear_animation.hpp b/include/rive/animation/linear_animation.hpp
index f7aa60e..01f9205 100644
--- a/include/rive/animation/linear_animation.hpp
+++ b/include/rive/animation/linear_animation.hpp
@@ -42,8 +42,21 @@
     /// work area start/end, speed, looping).
     float globalToLocalSeconds(float seconds) const;
 
+    const KeyedObject* getObject(size_t index) const
+    {
+        if (index < m_KeyedObjects.size())
+        {
+            return m_KeyedObjects[index].get();
+        }
+        else
+        {
+            return nullptr;
+        }
+    }
+
+    size_t numKeyedObjects() const { return m_KeyedObjects.size(); }
+
 #ifdef TESTING
-    size_t numKeyedObjects() { return m_KeyedObjects.size(); }
     // Used in testing to check how many animations gets deleted.
     static int deleteCount;
 #endif
diff --git a/include/rive/animation/state_instance.hpp b/include/rive/animation/state_instance.hpp
index 401b9f0..1dffd9d 100644
--- a/include/rive/animation/state_instance.hpp
+++ b/include/rive/animation/state_instance.hpp
@@ -22,7 +22,7 @@
     StateInstance(const LayerState* layerState);
     virtual ~StateInstance();
     virtual void advance(float seconds, StateMachineInstance* stateMachineInstance) = 0;
-    virtual void apply(float mix) = 0;
+    virtual void apply(ArtboardInstance* instance, float mix) = 0;
 
     /// Returns true when the State Machine needs to keep advancing this
     /// state.
diff --git a/include/rive/animation/system_state_instance.hpp b/include/rive/animation/system_state_instance.hpp
index b439da8..49f64b0 100644
--- a/include/rive/animation/system_state_instance.hpp
+++ b/include/rive/animation/system_state_instance.hpp
@@ -17,7 +17,7 @@
     SystemStateInstance(const LayerState* layerState, ArtboardInstance* instance);
 
     void advance(float seconds, StateMachineInstance* stateMachineInstance) override;
-    void apply(float mix) override;
+    void apply(ArtboardInstance* artboard, float mix) override;
 
     bool keepGoing() const override;
 };
diff --git a/include/rive/core/binary_data_reader.hpp b/include/rive/core/binary_data_reader.hpp
new file mode 100644
index 0000000..beae449
--- /dev/null
+++ b/include/rive/core/binary_data_reader.hpp
@@ -0,0 +1,38 @@
+#ifndef _RIVE_CORE_BINARY_DATA_READER_HPP_
+#define _RIVE_CORE_BINARY_DATA_READER_HPP_
+
+#include <string>
+#include <stdint.h>
+
+namespace rive
+{
+class BinaryDataReader
+{
+private:
+    uint8_t* m_Position;
+    uint8_t* m_End;
+    bool m_Overflowed;
+    size_t m_Length;
+
+    void overflow();
+
+public:
+    BinaryDataReader(uint8_t* bytes, size_t length);
+    bool didOverflow() const;
+    bool isEOF() const { return m_Position >= m_End; }
+    const uint8_t* position() const { return m_Position; }
+
+    size_t lengthInBytes() const;
+
+    uint64_t readVarUint();
+    uint32_t readVarUint32();
+    double readFloat64();
+    float readFloat32();
+    uint8_t readByte();
+    uint32_t readUint32();
+    void complete(uint8_t* bytes, size_t length);
+    void reset(uint8_t* bytes);
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/core/binary_reader.hpp b/include/rive/core/binary_reader.hpp
index c0c2c3b..00687da 100644
--- a/include/rive/core/binary_reader.hpp
+++ b/include/rive/core/binary_reader.hpp
@@ -49,6 +49,7 @@
         }
         return static_cast<T>(value);
     }
+    void reset();
 };
 } // namespace rive
 
diff --git a/include/rive/core/binary_stream.hpp b/include/rive/core/binary_stream.hpp
new file mode 100644
index 0000000..f582ccf
--- /dev/null
+++ b/include/rive/core/binary_stream.hpp
@@ -0,0 +1,20 @@
+#ifndef _RIVE_CORE_BINARY_STREAM_HPP_
+#define _RIVE_CORE_BINARY_STREAM_HPP_
+
+#include <cstdint>
+#include <cstddef>
+
+namespace rive
+{
+// Used to write binary chunks to an underlying stream, makes no assumptions
+// regarding storage/streaming it can flush the contents as it needs.
+class BinaryStream
+{
+public:
+    virtual void write(const uint8_t* bytes, std::size_t length) = 0;
+    virtual void flush() = 0;
+    virtual void clear() = 0;
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/core/binary_writer.hpp b/include/rive/core/binary_writer.hpp
new file mode 100644
index 0000000..213e295
--- /dev/null
+++ b/include/rive/core/binary_writer.hpp
@@ -0,0 +1,31 @@
+
+#ifndef _RIVE_CORE_BINARY_WRITER_HPP_
+#define _RIVE_CORE_BINARY_WRITER_HPP_
+
+#include <cstddef>
+#include <cstdint>
+
+namespace rive
+{
+class BinaryStream;
+class BinaryWriter
+{
+private:
+    BinaryStream* m_Stream;
+
+public:
+    BinaryWriter(BinaryStream* stream);
+    ~BinaryWriter();
+    void write(float value);
+    void writeFloat(float value);
+    void write(double value);
+    void writeVarUint(uint64_t value);
+    void writeVarUint(uint32_t value);
+    void write(const uint8_t* bytes, std::size_t length);
+    void write(uint8_t value);
+    void writeDouble(double value);
+    void clear();
+};
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/include/rive/core/reader.h b/include/rive/core/reader.h
index f6969ba..45c8ffd 100644
--- a/include/rive/core/reader.h
+++ b/include/rive/core/reader.h
@@ -35,6 +35,29 @@
     return p - buf;
 }
 
+/* Decode an unsigned int LEB128 at buf into r, returning the nr of bytes read.
+ */
+inline size_t decode_uint_leb32(const uint8_t* buf, const uint8_t* buf_end, uint32_t* r)
+{
+    const uint8_t* p = buf;
+    uint8_t shift = 0;
+    uint32_t result = 0;
+    uint8_t byte;
+
+    do
+    {
+        if (p >= buf_end)
+        {
+            return 0;
+        }
+        byte = *p++;
+        result |= ((uint32_t)(byte & 0x7f)) << shift;
+        shift += 7;
+    } while ((byte & 0x80) != 0);
+    *r = result;
+    return p - buf;
+}
+
 /* Decodes a string
  */
 inline uint64_t decode_string(uint64_t str_len,
@@ -57,6 +80,27 @@
     return str_len;
 }
 
+/* Decodes a double (8 bytes)
+ */
+inline size_t decode_double(const uint8_t* buf, const uint8_t* buf_end, double* r)
+{
+    // Return zero bytes read on buffer overflow
+    if (buf_end - buf < sizeof(double))
+    {
+        return 0;
+    }
+    if (is_big_endian())
+    {
+        uint8_t inverted[8] = {buf[7], buf[6], buf[5], buf[4], buf[3], buf[2], buf[1], buf[0]};
+        memcpy(r, inverted, sizeof(double));
+    }
+    else
+    {
+        memcpy(r, buf, sizeof(double));
+    }
+    return sizeof(double);
+}
+
 /* Decodes a float (4 bytes)
  */
 inline size_t decode_float(const uint8_t* buf, const uint8_t* buf_end, float* r)
@@ -110,4 +154,4 @@
         memcpy(r, buf, sizeof(uint32_t));
     }
     return sizeof(uint32_t);
-}
+}
\ No newline at end of file
diff --git a/include/rive/core/vector_binary_writer.hpp b/include/rive/core/vector_binary_writer.hpp
new file mode 100644
index 0000000..34ba649
--- /dev/null
+++ b/include/rive/core/vector_binary_writer.hpp
@@ -0,0 +1,43 @@
+#ifndef _RIVE_CORE_VECTOR_BINARY_WRITER_HPP_
+#define _RIVE_CORE_VECTOR_BINARY_WRITER_HPP_
+
+#include "rive/core/binary_stream.hpp"
+#include "rive/core/binary_writer.hpp"
+#include <cstring>
+
+namespace rive
+{
+class VectorBinaryWriter : public BinaryStream, public BinaryWriter
+{
+private:
+    std::vector<uint8_t>* m_WriteBuffer;
+    std::size_t m_Start;
+    size_t m_pos = 0;
+
+public:
+    VectorBinaryWriter(std::vector<uint8_t>* buffer) :
+        BinaryWriter(this), m_WriteBuffer(buffer), m_Start(m_WriteBuffer->size())
+    {}
+
+    uint8_t* buffer() const { return &(*m_WriteBuffer)[m_Start]; }
+    std::size_t bufferSize() const { return m_WriteBuffer->size() - m_Start; }
+
+    std::size_t start() const { return m_Start; }
+    size_t size() const { return m_pos; }
+
+    using BinaryWriter::write;
+    void write(const uint8_t* bytes, std::size_t length) override
+    {
+        auto end = m_pos;
+        if (m_WriteBuffer->size() < end + length)
+        {
+            m_WriteBuffer->resize(end + length);
+        }
+        std::memcpy(&((*m_WriteBuffer)[end]), bytes, length);
+        m_pos += length;
+    }
+    void flush() override {}
+    void clear() override { m_pos = 0; }
+};
+} // namespace rive
+#endif
\ No newline at end of file
diff --git a/src/animation/animation_reset.cpp b/src/animation/animation_reset.cpp
new file mode 100644
index 0000000..87b906a
--- /dev/null
+++ b/src/animation/animation_reset.cpp
@@ -0,0 +1,49 @@
+#include "rive/animation/animation_reset.hpp"
+#include "rive/core/vector_binary_writer.hpp"
+#include "rive/generated/core_registry.hpp"
+
+using namespace rive;
+
+AnimationReset::AnimationReset() : m_binaryWriter(&m_WriteBuffer), m_binaryReader(nullptr, 0) {}
+
+void AnimationReset::writeObjectId(uint32_t objectId) { m_binaryWriter.writeVarUint(objectId); }
+
+void AnimationReset::writeTotalProperties(uint8_t value) { m_binaryWriter.write(value); }
+
+void AnimationReset::writePropertyKey(uint8_t value) { m_binaryWriter.write(value); }
+
+void AnimationReset::writePropertyValue(float value) { m_binaryWriter.writeFloat(value); }
+
+void AnimationReset::clear() { m_binaryWriter.clear(); }
+
+void AnimationReset::complete()
+{
+    m_binaryReader.complete(&m_WriteBuffer.front(), m_binaryWriter.size());
+}
+
+void AnimationReset::apply(Artboard* artboard)
+{
+    m_binaryReader.reset(&m_WriteBuffer.front());
+    while (!m_binaryReader.isEOF())
+    {
+        auto objectId = m_binaryReader.readVarUint32();
+        auto object = artboard->resolve(objectId);
+        auto totalProperties = m_binaryReader.readByte();
+        auto currentPropertyIndex = 0;
+        while (currentPropertyIndex < totalProperties)
+        {
+            auto propertyKey = m_binaryReader.readByte();
+            auto propertyValue = m_binaryReader.readFloat32();
+            switch (CoreRegistry::propertyFieldId(propertyKey))
+            {
+                case CoreDoubleType::id:
+                    CoreRegistry::setDouble(object, propertyKey, propertyValue);
+                    break;
+                case CoreColorType::id:
+                    CoreRegistry::setColor(object, propertyKey, propertyValue);
+                    break;
+            }
+            currentPropertyIndex++;
+        }
+    }
+}
diff --git a/src/animation/animation_reset_factory.cpp b/src/animation/animation_reset_factory.cpp
new file mode 100644
index 0000000..a08f35f
--- /dev/null
+++ b/src/animation/animation_reset_factory.cpp
@@ -0,0 +1,210 @@
+#include "rive/animation/animation_reset_factory.hpp"
+#include "rive/animation/linear_animation.hpp"
+#include "rive/animation/animation_state.hpp"
+#include "rive/animation/keyed_object.hpp"
+#include "rive/animation/keyed_property.hpp"
+#include "rive/generated/core_registry.hpp"
+#include <map>
+#include <set>
+
+using namespace rive;
+
+class KeyedPropertyData
+{
+public:
+    const KeyedProperty* keyedProperty;
+    bool isBaseline;
+    KeyedPropertyData(const KeyedProperty* value, bool baselineValue) :
+        keyedProperty(value), isBaseline(baselineValue)
+    {}
+};
+
+class KeyedObjectData
+{
+public:
+    std::vector<KeyedPropertyData> keyedPropertiesData;
+    std::set<int> keyedPropertiesSet;
+    uint32_t objectId;
+    KeyedObjectData(const uint32_t value) { objectId = value; }
+    void addProperties(const KeyedObject* keyedObject, bool isBaseline)
+    {
+        size_t index = 0;
+        while (index < keyedObject->numKeyedProperties())
+        {
+            auto keyedProperty = keyedObject->getProperty(index);
+            auto pos = keyedPropertiesSet.find(keyedProperty->propertyKey());
+            if (pos == keyedPropertiesSet.end())
+            {
+                keyedPropertiesSet.insert(keyedProperty->propertyKey());
+                keyedPropertiesData.push_back(KeyedPropertyData(keyedProperty, isBaseline));
+            }
+            index++;
+        }
+    }
+};
+
+class AnimationsData
+{
+
+private:
+    std::vector<std::unique_ptr<KeyedObjectData>> keyedObjectsData;
+    KeyedObjectData* getKeyedObjectData(const KeyedObject* keyedObject)
+    {
+        for (auto& keyedObjectData : keyedObjectsData)
+        {
+            if (keyedObjectData->objectId == keyedObject->objectId())
+            {
+                return keyedObjectData.get();
+            }
+        }
+
+        auto keyedObjectData = rivestd::make_unique<KeyedObjectData>(keyedObject->objectId());
+        auto ref = keyedObjectData.get();
+        keyedObjectsData.push_back(std::move(keyedObjectData));
+        return ref;
+    }
+
+    void findKeyedObjects(const LinearAnimation* animation, bool isFirstAnimation)
+    {
+
+        size_t index = 0;
+        while (index < animation->numKeyedObjects())
+        {
+            auto keyedObject = animation->getObject(index);
+            auto keyedObjectData = getKeyedObjectData(keyedObject);
+
+            keyedObjectData->addProperties(keyedObject, isFirstAnimation);
+            index++;
+        }
+    }
+
+public:
+    AnimationsData(std::vector<const LinearAnimation*>& animations, bool useFirstAsBaseline)
+    {
+        bool isFirstAnimation = useFirstAsBaseline;
+        for (auto animation : animations)
+        {
+            findKeyedObjects(animation, isFirstAnimation);
+            isFirstAnimation = false;
+        }
+    }
+
+    void writeObjects(AnimationReset* animationReset, ArtboardInstance* artboard)
+    {
+        for (auto& keyedObjectData : keyedObjectsData)
+        {
+            auto object = artboard->resolve(keyedObjectData->objectId)->as<Component>();
+            auto propertiesData = keyedObjectData->keyedPropertiesData;
+            if (propertiesData.size() > 0)
+            {
+                animationReset->writeObjectId(keyedObjectData->objectId);
+                animationReset->writeTotalProperties(propertiesData.size());
+                for (auto keyedPropertyData : propertiesData)
+                {
+                    auto keyedProperty = keyedPropertyData.keyedProperty;
+                    auto propertyKey = keyedProperty->propertyKey();
+                    switch (CoreRegistry::propertyFieldId(propertyKey))
+                    {
+                        case CoreDoubleType::id:
+                            animationReset->writePropertyKey(propertyKey);
+                            if (keyedPropertyData.isBaseline)
+                            {
+                                auto firstKeyframe = keyedProperty->first();
+                                if (firstKeyframe != nullptr)
+                                {
+                                    auto value =
+                                        keyedProperty->first()->as<KeyFrameDouble>()->value();
+                                    animationReset->writePropertyValue(value);
+                                }
+                            }
+                            else
+                            {
+                                animationReset->writePropertyValue(
+                                    CoreRegistry::getDouble(object, propertyKey));
+                            }
+                            break;
+                        case CoreColorType::id:
+
+                            animationReset->writePropertyKey(propertyKey);
+                            if (keyedPropertyData.isBaseline)
+                            {
+                                auto firstKeyframe = keyedProperty->first();
+                                if (firstKeyframe != nullptr)
+                                {
+                                    auto value =
+                                        keyedProperty->first()->as<KeyFrameColor>()->value();
+                                    animationReset->writePropertyValue(value);
+                                }
+                            }
+                            else
+                            {
+                                animationReset->writePropertyValue(
+                                    CoreRegistry::getColor(object, propertyKey));
+                            }
+                            break;
+                    }
+                }
+            }
+        }
+        animationReset->complete();
+    }
+};
+
+std::unique_ptr<AnimationReset> AnimationResetFactory::getInstance()
+{
+    std::unique_lock<std::mutex> lock(m_mutex);
+    if (m_resources.size() > 0)
+    {
+        auto instance = std::move(m_resources.back());
+        m_resources.pop_back();
+        return instance;
+    }
+    auto instance = rivestd::make_unique<AnimationReset>();
+    return instance;
+}
+
+void AnimationResetFactory::fromState(StateInstance* stateInstance,
+                                      std::vector<const LinearAnimation*>& animations)
+{
+    if (stateInstance != nullptr)
+    {
+        auto state = stateInstance->state();
+        if (state->is<AnimationState>())
+        {
+            animations.push_back(state->as<AnimationState>()->animation());
+        }
+    }
+}
+
+std::unique_ptr<AnimationReset> AnimationResetFactory::fromStates(StateInstance* stateFrom,
+                                                                  StateInstance* currentState,
+                                                                  ArtboardInstance* artboard)
+{
+    std::vector<const LinearAnimation*> animations;
+    fromState(stateFrom, animations);
+    fromState(currentState, animations);
+    return fromAnimations(animations, artboard, false);
+}
+
+std::unique_ptr<AnimationReset> AnimationResetFactory::fromAnimations(
+    std::vector<const LinearAnimation*>& animations,
+    ArtboardInstance* artboard,
+    bool useFirstAsBaseline)
+{
+    auto animationsData = new AnimationsData(animations, useFirstAsBaseline);
+    auto animationReset = AnimationResetFactory::getInstance();
+    animationsData->writeObjects(animationReset.get(), artboard);
+    delete animationsData;
+    return animationReset;
+}
+
+std::vector<std::unique_ptr<AnimationReset>> AnimationResetFactory::m_resources;
+
+std::mutex AnimationResetFactory::m_mutex;
+
+void AnimationResetFactory::release(std::unique_ptr<AnimationReset> value)
+{
+    std::unique_lock<std::mutex> lock(m_mutex);
+    value->clear();
+    m_resources.push_back(std::move(value));
+}
diff --git a/src/animation/animation_state_instance.cpp b/src/animation/animation_state_instance.cpp
index 0739550..5cae3e8 100644
--- a/src/animation/animation_state_instance.cpp
+++ b/src/animation/animation_state_instance.cpp
@@ -30,7 +30,10 @@
                                               stateMachineInstance);
 }
 
-void AnimationStateInstance::apply(float mix) { m_AnimationInstance.apply(mix); }
+void AnimationStateInstance::apply(ArtboardInstance* instance, float mix)
+{
+    m_AnimationInstance.apply(mix);
+}
 
 bool AnimationStateInstance::keepGoing() const { return m_KeepGoing; }
 void AnimationStateInstance::clearSpilledTime() { m_AnimationInstance.clearSpilledTime(); }
\ No newline at end of file
diff --git a/src/animation/blend_state_1d_instance.cpp b/src/animation/blend_state_1d_instance.cpp
index 6902273..f1049ba 100644
--- a/src/animation/blend_state_1d_instance.cpp
+++ b/src/animation/blend_state_1d_instance.cpp
@@ -1,12 +1,33 @@
 #include "rive/animation/blend_state_1d_instance.hpp"
 #include "rive/animation/state_machine_input_instance.hpp"
+#include "rive/animation/layer_state_flags.hpp"
 
 using namespace rive;
 
 BlendState1DInstance::BlendState1DInstance(const BlendState1D* blendState,
                                            ArtboardInstance* instance) :
     BlendStateInstance<BlendState1D, BlendAnimation1D>(blendState, instance)
-{}
+{
+
+    if ((static_cast<LayerStateFlags>(blendState->flags()) & LayerStateFlags::Reset) ==
+        LayerStateFlags::Reset)
+    {
+        auto animations = std::vector<const LinearAnimation*>();
+        for (auto blendAnimation : blendState->animations())
+        {
+            animations.push_back(blendAnimation->animation());
+        }
+        m_AnimationReset = AnimationResetFactory::fromAnimations(animations, instance, true);
+    }
+}
+
+BlendState1DInstance::~BlendState1DInstance()
+{
+    if (m_AnimationReset != nullptr)
+    {
+        AnimationResetFactory::release(std::move(m_AnimationReset));
+    }
+}
 
 int BlendState1DInstance::animationIndex(float value)
 {
@@ -39,6 +60,15 @@
     return idx;
 }
 
+void BlendState1DInstance::apply(ArtboardInstance* instance, float mix)
+{
+    if (m_AnimationReset != nullptr)
+    {
+        m_AnimationReset->apply(instance);
+    }
+    BlendStateInstance::apply(instance, mix);
+}
+
 void BlendState1DInstance::advance(float seconds, StateMachineInstance* stateMachineInstance)
 {
     BlendStateInstance<BlendState1D, BlendAnimation1D>::advance(seconds, stateMachineInstance);
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp
index e70d4f4..51f01f1 100644
--- a/src/animation/state_machine_instance.cpp
+++ b/src/animation/state_machine_instance.cpp
@@ -1,3 +1,5 @@
+#include "rive/animation/animation_reset.hpp"
+#include "rive/animation/animation_reset_factory.hpp"
 #include "rive/animation/animation_state_instance.hpp"
 #include "rive/animation/animation_state.hpp"
 #include "rive/animation/any_state.hpp"
@@ -70,6 +72,7 @@
             if (m_mix == 1.0f && !m_transitionCompleted)
             {
                 m_transitionCompleted = true;
+                clearAnimationReset();
                 fireEvents(StateMachineFireOccurance::atEnd, m_transition->events());
             }
         }
@@ -256,6 +259,21 @@
         return nullptr;
     }
 
+    void buildAnimationResetForTransition()
+    {
+        m_animationReset =
+            AnimationResetFactory::fromStates(m_stateFrom, m_currentState, m_artboardInstance);
+    }
+
+    void clearAnimationReset()
+    {
+        if (m_animationReset != nullptr)
+        {
+            AnimationResetFactory::release(std::move(m_animationReset));
+            m_animationReset = nullptr;
+        }
+    }
+
     bool tryChangeState(StateInstance* stateFromInstance, bool ignoreTriggers)
     {
         if (stateFromInstance == nullptr)
@@ -266,6 +284,7 @@
         auto transition = findAllowedTransition(stateFromInstance, ignoreTriggers);
         if (transition != nullptr)
         {
+            clearAnimationReset();
             changeState(transition->stateTo());
             m_stateMachineChangedOnAdvance = true;
             // state actually has changed
@@ -288,6 +307,11 @@
             }
             m_stateFrom = outState;
 
+            if (!m_transitionCompleted)
+            {
+                buildAnimationResetForTransition();
+            }
+
             // If we had an exit time and wanted to pause on exit, make
             // sure to hold the exit time. Delegate this to the
             // transition by telling it that it was completed.
@@ -327,6 +351,10 @@
 
     void apply(/*Artboard* artboard*/)
     {
+        if (m_animationReset != nullptr)
+        {
+            m_animationReset->apply(m_artboardInstance);
+        }
         if (m_holdAnimation != nullptr)
         {
             m_holdAnimation->apply(m_artboardInstance, m_holdTime, m_mixFrom);
@@ -342,12 +370,12 @@
         if (m_stateFrom != nullptr && m_mix < 1.0f)
         {
             auto fromMix = cubic != nullptr ? cubic->transform(m_mixFrom) : m_mixFrom;
-            m_stateFrom->apply(fromMix);
+            m_stateFrom->apply(m_artboardInstance, fromMix);
         }
         if (m_currentState != nullptr)
         {
             auto mix = cubic != nullptr ? cubic->transform(m_mix) : m_mix;
-            m_currentState->apply(mix);
+            m_currentState->apply(m_artboardInstance, mix);
         }
     }
 
@@ -378,6 +406,7 @@
     StateInstance* m_stateFrom = nullptr;
 
     const StateTransition* m_transition = nullptr;
+    std::unique_ptr<AnimationReset> m_animationReset = nullptr;
     bool m_transitionCompleted = false;
 
     bool m_holdAnimationFrom = false;
diff --git a/src/animation/system_state_instance.cpp b/src/animation/system_state_instance.cpp
index 92e28de..4eb489a 100644
--- a/src/animation/system_state_instance.cpp
+++ b/src/animation/system_state_instance.cpp
@@ -6,6 +6,6 @@
 {}
 
 void SystemStateInstance::advance(float seconds, StateMachineInstance* stateMachineInstance) {}
-void SystemStateInstance::apply(float mix) {}
+void SystemStateInstance::apply(ArtboardInstance* artboard, float mix) {}
 
 bool SystemStateInstance::keepGoing() const { return false; }
\ No newline at end of file
diff --git a/src/core/binary_data_reader.cpp b/src/core/binary_data_reader.cpp
new file mode 100644
index 0000000..1fa7f33
--- /dev/null
+++ b/src/core/binary_data_reader.cpp
@@ -0,0 +1,102 @@
+#include "rive/core/binary_data_reader.hpp"
+#include "rive/core/reader.h"
+
+using namespace rive;
+
+BinaryDataReader::BinaryDataReader(uint8_t* bytes, size_t length) :
+    m_Position(bytes), m_End(bytes + length), m_Overflowed(false), m_Length(length)
+{}
+
+size_t BinaryDataReader::lengthInBytes() const { return m_Length; }
+
+bool BinaryDataReader::didOverflow() const { return m_Overflowed; }
+
+void BinaryDataReader::overflow()
+{
+    m_Overflowed = true;
+    m_Position = m_End;
+}
+
+uint64_t BinaryDataReader::readVarUint()
+{
+    uint64_t value;
+    auto readBytes = decode_uint_leb(m_Position, m_End, &value);
+    if (readBytes == 0)
+    {
+        overflow();
+        return 0;
+    }
+    m_Position += readBytes;
+    return value;
+}
+
+uint32_t BinaryDataReader::readVarUint32()
+{
+    uint32_t value;
+    auto readBytes = decode_uint_leb32(m_Position, m_End, &value);
+    if (readBytes == 0)
+    {
+        overflow();
+        return 0;
+    }
+    m_Position += readBytes;
+    return value;
+}
+
+double BinaryDataReader::readFloat64()
+{
+    double value;
+    auto readBytes = decode_double(m_Position, m_End, &value);
+    if (readBytes == 0)
+    {
+        overflow();
+        return 0.0;
+    }
+    m_Position += readBytes;
+    return value;
+}
+
+float BinaryDataReader::readFloat32()
+{
+    float value;
+    auto readBytes = decode_float(m_Position, m_End, &value);
+    if (readBytes == 0)
+    {
+        overflow();
+        return 0.0f;
+    }
+    m_Position += readBytes;
+    return value;
+}
+
+uint8_t BinaryDataReader::readByte()
+{
+    if (m_End - m_Position < 1)
+    {
+        overflow();
+        return 0;
+    }
+    return *m_Position++;
+}
+
+uint32_t BinaryDataReader::readUint32()
+{
+    uint32_t value;
+    auto readBytes = decode_uint_32(m_Position, m_End, &value);
+    if (readBytes == 0)
+    {
+        overflow();
+        return 0;
+    }
+    m_Position += readBytes;
+    return value;
+}
+
+void BinaryDataReader::complete(uint8_t* bytes, size_t length)
+{
+    m_Position = bytes;
+    m_End = bytes + length;
+    m_Length = length;
+}
+
+void BinaryDataReader::reset(uint8_t* bytes) { m_Position = bytes; }
diff --git a/src/core/binary_reader.cpp b/src/core/binary_reader.cpp
index d00413f..ee3ca01 100644
--- a/src/core/binary_reader.cpp
+++ b/src/core/binary_reader.cpp
@@ -113,3 +113,5 @@
     m_Position += readBytes;
     return value;
 }
+
+void BinaryReader::reset() { m_Position = m_Bytes.begin(); }
diff --git a/src/core/binary_writer.cpp b/src/core/binary_writer.cpp
new file mode 100644
index 0000000..ef0fd68
--- /dev/null
+++ b/src/core/binary_writer.cpp
@@ -0,0 +1,120 @@
+#include "rive/core/binary_writer.hpp"
+#include "rive/core/binary_stream.hpp"
+#include "rive/core/reader.h"
+#include <stdio.h>
+
+using namespace rive;
+
+BinaryWriter::BinaryWriter(BinaryStream* stream) : m_Stream(stream) {}
+BinaryWriter::~BinaryWriter() { m_Stream->flush(); }
+
+void BinaryWriter::write(float value)
+{
+    auto bytes = reinterpret_cast<uint8_t*>(&value);
+    if (is_big_endian())
+    {
+        uint8_t backwards[4] = {bytes[3], bytes[2], bytes[1], bytes[0]};
+        m_Stream->write(backwards, 4);
+    }
+    else
+    {
+        m_Stream->write(bytes, 4);
+    }
+}
+
+void BinaryWriter::writeFloat(float value)
+{
+    auto bytes = reinterpret_cast<uint8_t*>(&value);
+    if (is_big_endian())
+    {
+        uint8_t backwards[4] = {bytes[3], bytes[2], bytes[1], bytes[0]};
+        m_Stream->write(backwards, 4);
+    }
+    else
+    {
+        m_Stream->write(bytes, 4);
+    }
+}
+
+void BinaryWriter::write(double value)
+{
+    auto bytes = reinterpret_cast<uint8_t*>(&value);
+    if (is_big_endian())
+    {
+        uint8_t backwards[8] =
+            {bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]};
+        m_Stream->write(backwards, 8);
+    }
+    else
+    {
+        m_Stream->write(bytes, 8);
+    }
+}
+
+void BinaryWriter::writeVarUint(uint64_t value)
+{
+    uint8_t buffer[16];
+    int index = 0;
+    do
+    {
+        uint8_t byte = value & 0x7f;
+        value >>= 7;
+
+        if (value != 0)
+        {
+            // more bytes follow
+            byte |= 0x80;
+        }
+
+        buffer[index++] = byte;
+    } while (value != 0);
+    m_Stream->write(buffer, index);
+}
+
+void BinaryWriter::writeVarUint(uint32_t value)
+{
+    uint8_t buffer[16];
+    int index = 0;
+    do
+    {
+        uint8_t byte = value & 0x7f;
+        value >>= 7;
+
+        if (value != 0)
+        {
+            // more bytes follow
+            byte |= 0x80;
+        }
+
+        buffer[index++] = byte;
+    } while (value != 0);
+    m_Stream->write(buffer, index);
+}
+
+void BinaryWriter::write(const uint8_t* bytes, size_t length)
+{
+    if (length == 0)
+    {
+        return;
+    }
+    m_Stream->write(bytes, length);
+}
+
+void BinaryWriter::write(uint8_t value) { m_Stream->write(&value, 1); }
+
+void BinaryWriter::writeDouble(double value)
+{
+    auto bytes = reinterpret_cast<uint8_t*>(&value);
+    if (is_big_endian())
+    {
+        uint8_t backwards[8] =
+            {bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]};
+        m_Stream->write(backwards, 8);
+    }
+    else
+    {
+        m_Stream->write(bytes, 8);
+    }
+}
+
+void BinaryWriter::clear() { m_Stream->clear(); }
\ No newline at end of file
diff --git a/test/assets/animation_reset_cases.riv b/test/assets/animation_reset_cases.riv
new file mode 100644
index 0000000..09e544f
--- /dev/null
+++ b/test/assets/animation_reset_cases.riv
Binary files differ
diff --git a/test/state_machine_test.cpp b/test/state_machine_test.cpp
index e3dd9cf..6f25ae8 100644
--- a/test/state_machine_test.cpp
+++ b/test/state_machine_test.cpp
@@ -10,6 +10,7 @@
 #include <rive/animation/blend_animation_1d.hpp>
 #include <rive/animation/blend_state_direct.hpp>
 #include <rive/animation/blend_state_transition.hpp>
+#include <rive/animation/animation_reset_factory.hpp>
 #include <rive/shapes/paint/solid_color.hpp>
 #include <rive/shapes/paint/stroke.hpp>
 #include <rive/shapes/shape.hpp>
@@ -242,3 +243,174 @@
 
     delete stateMachineInstance;
 }
+
+TEST_CASE("Blend state animations with reset applied to them.", "[file]")
+{
+    auto file = ReadRiveFile("../../test/assets/animation_reset_cases.riv");
+
+    auto artboard = file->artboard();
+    auto stateMachine = artboard->stateMachine("blend-states-state-machine");
+
+    // We empty all factory reset resources to start the test clean
+    rive::AnimationResetFactory::releaseResources();
+    REQUIRE(rive::AnimationResetFactory::resourcesCount() == 0);
+
+    REQUIRE(artboard != nullptr);
+    REQUIRE(artboard->animationCount() == 9);
+    REQUIRE(artboard->stateMachineCount() == 1);
+
+    auto abi = artboard->instance();
+    rive::StateMachineInstance* stateMachineInstance =
+        new rive::StateMachineInstance(stateMachine, abi.get());
+    stateMachineInstance->advanceAndApply(0.1f);
+    abi->advance(0.1f);
+
+    auto blendValueNumber = stateMachineInstance->getNumber("blend-value");
+    REQUIRE(blendValueNumber != nullptr);
+    REQUIRE(blendValueNumber->value() == 0);
+    blendValueNumber->value(50);
+    REQUIRE(blendValueNumber->value() == 50);
+    stateMachineInstance->advanceAndApply(0.1f);
+    abi->advance(0.1f);
+
+    auto rect1 = abi->children()[9]->as<rive::Shape>();
+    REQUIRE(rect1->name() == "rect1");
+
+    auto rect2 = abi->children()[7]->as<rive::Shape>();
+    REQUIRE(rect2->name() == "rect2");
+
+    auto triangle = abi->children()[5]->as<rive::Shape>();
+    REQUIRE(triangle->name() == "triangle");
+
+    // This blend rotates 2 * Pi. At 50% it should have rotated 1 * Pi
+    REQUIRE(rect1->rotation() == Approx(3.141592f));
+
+    auto state2Bool = stateMachineInstance->getBool("state-2");
+    REQUIRE(state2Bool != nullptr);
+    REQUIRE(state2Bool->value() == false);
+    state2Bool->value(true);
+    blendValueNumber->value(50);
+    stateMachineInstance->advanceAndApply(0.1f);
+    abi->advance(0.1f);
+    stateMachineInstance->advanceAndApply(0.1f);
+    REQUIRE(rive::AnimationResetFactory::resourcesCount() == 0);
+    REQUIRE(state2Bool->value() == true);
+    // X and Y interpolation in this state ranges are [75; 425] and [50; 450]
+    // so at 50% they should be at 250, 250
+    REQUIRE(rect1->x() == 250.0f);
+    REQUIRE(rect1->y() == 250.0f);
+    // rect2 rotation range is [0; -360]
+    // so at 50% it should be at -180
+    REQUIRE(rect2->rotation() == Approx(-3.141592f));
+
+    auto state3Bool = stateMachineInstance->getBool("state-3");
+    REQUIRE(state3Bool != nullptr);
+    REQUIRE(state3Bool->value() == false);
+    state3Bool->value(true);
+    REQUIRE(rive::AnimationResetFactory::resourcesCount() == 0);
+    stateMachineInstance->advanceAndApply(0.1f);
+    abi->advance(0.1f);
+    stateMachineInstance->advanceAndApply(0.1f);
+    blendValueNumber->value(100);
+    abi->advance(0.1f);
+    stateMachineInstance->advanceAndApply(0.1f);
+    REQUIRE(state3Bool->value() == true);
+    REQUIRE(triangle->y() == Approx(43.13281f));
+
+    REQUIRE(rive::AnimationResetFactory::resourcesCount() == 1);
+
+    auto state4Bool = stateMachineInstance->getBool("state-4");
+    REQUIRE(state4Bool != nullptr);
+    REQUIRE(state4Bool->value() == false);
+    state4Bool->value(true);
+    stateMachineInstance->advanceAndApply(0.1f);
+    abi->advance(0.1f);
+    REQUIRE(state4Bool->value() == true);
+    REQUIRE(rive::AnimationResetFactory::resourcesCount() == 2);
+
+    state4Bool->value(false);
+    stateMachineInstance->advanceAndApply(0.1f);
+    abi->advance(0.1f);
+    stateMachineInstance->advanceAndApply(0.1f);
+    REQUIRE(state4Bool->value() == false);
+    // After switching states mutiple times resources stay at 2 because they are released
+    // and retrieved from the pool
+    REQUIRE(rive::AnimationResetFactory::resourcesCount() == 2);
+    delete stateMachineInstance;
+}
+
+TEST_CASE("Transitions with reset applied to them.", "[file]")
+{
+    auto file = ReadRiveFile("../../test/assets/animation_reset_cases.riv");
+
+    auto artboard = file->artboard("transitions");
+    auto stateMachine = artboard->stateMachine("transitions-state-machine");
+
+    // We empty all factory reset resources to start the test clean
+    rive::AnimationResetFactory::releaseResources();
+    REQUIRE(rive::AnimationResetFactory::resourcesCount() == 0);
+
+    REQUIRE(artboard != nullptr);
+    REQUIRE(artboard->animationCount() == 5);
+    REQUIRE(artboard->stateMachineCount() == 1);
+
+    auto abi = artboard->instance();
+    rive::StateMachineInstance* stateMachineInstance =
+        new rive::StateMachineInstance(stateMachine, abi.get());
+    stateMachineInstance->advanceAndApply(0.1f);
+    abi->advance(0.1f);
+
+    auto rect = abi->children()[7]->as<rive::Shape>();
+    REQUIRE(rect->name() == "rectangle");
+    REQUIRE(rect->x() == 50);
+
+    auto ellipse = abi->children()[5]->as<rive::Shape>();
+    REQUIRE(ellipse->name() == "ellipse");
+    REQUIRE(ellipse->x() == Approx(440.31241));
+
+    auto stateNumber = stateMachineInstance->getNumber("Number 1");
+    REQUIRE(stateNumber != nullptr);
+    REQUIRE(stateNumber->value() == 0);
+    stateNumber->value(1);
+    REQUIRE(stateNumber->value() == 1);
+    stateMachineInstance->advanceAndApply(0.1f);
+    abi->advance(0.1f);
+    stateMachineInstance->advanceAndApply(1.25f);
+
+    // rect transitions in 2.5 secs from x->50 to x->433
+    // so if the translation is linear, after 1.25s it should have
+    // traversed half the path
+    REQUIRE(rect->x() == 241.5f);
+
+    stateNumber->value(2);
+    REQUIRE(stateNumber->value() == 2);
+    stateMachineInstance->advanceAndApply(0.1f);
+    abi->advance(0.1f);
+    stateMachineInstance->advanceAndApply(1.25f);
+    // range is [440.31241; 42.69]
+    // half if the path is 42.69 + (440.21241 - 42.60) = 241.4962
+    REQUIRE(ellipse->x() == Approx(241.49992f));
+
+    // Transitions release their instance immediately so it's available for the next instance to use
+    REQUIRE(rive::AnimationResetFactory::resourcesCount() == 0);
+
+    stateNumber->value(3);
+    REQUIRE(stateNumber->value() == 3);
+    stateMachineInstance->advanceAndApply(0.1f);
+    abi->advance(0.1f);
+    stateMachineInstance->advanceAndApply(1.25f);
+
+    REQUIRE(rive::AnimationResetFactory::resourcesCount() == 0);
+
+    stateNumber->value(4);
+    REQUIRE(stateNumber->value() == 4);
+    stateMachineInstance->advanceAndApply(0.1f);
+    abi->advance(0.1f);
+    stateMachineInstance->advanceAndApply(1.25f);
+
+    // The last two states don't have a transition with duration set so the instance is released
+    // and available
+    REQUIRE(rive::AnimationResetFactory::resourcesCount() == 1);
+
+    delete stateMachineInstance;
+}