blob: da041e6ce5b40b50fb34f6d92511d44238f8a830 [file] [edit]
#include "rive/animation/blend_accumulator.hpp"
#include "rive/generated/core_registry.hpp"
using namespace rive;
static uint32_t slotOf(const Core* object, int propertyKey)
{
// Fibonacci hashing of the pointer, with the key folded in.
uint64_t h = ((uint64_t)(uintptr_t)object ^
((uint64_t)(uint32_t)propertyKey << 48)) *
UINT64_C(0x9E3779B97F4A7C15);
return (uint32_t)(h >> 32);
}
uint32_t BlendAccumulator::find(Core* object, int propertyKey) const
{
if (m_index.empty())
{
return 0;
}
const uint32_t mask = (uint32_t)m_index.size() - 1;
for (uint32_t i = slotOf(object, propertyKey) & mask;; i = (i + 1) & mask)
{
uint32_t entry = m_index[i];
if (entry == 0)
{
return 0;
}
const Value& value = m_values[entry - 1];
if (value.object == object && value.propertyKey == propertyKey)
{
return entry;
}
}
}
void BlendAccumulator::index(uint32_t valueIndex)
{
const uint32_t mask = (uint32_t)m_index.size() - 1;
const Value& value = m_values[valueIndex];
uint32_t i = slotOf(value.object, value.propertyKey) & mask;
while (m_index[i] != 0)
{
i = (i + 1) & mask;
}
m_index[i] = valueIndex + 1;
}
BlendAccumulator::Value& BlendAccumulator::value(
Core* object,
int propertyKey,
bool isColor,
bool seeding)
{
uint32_t entry = find(object, propertyKey);
if (entry == 0)
{
m_values.push_back({object, propertyKey, isColor, 0, 0.0f, 0});
if (m_values.size() * 2 > m_index.size())
{
m_index.assign(m_index.empty() ? 16 : m_index.size() * 2, 0);
for (uint32_t i = 0; i < (uint32_t)m_values.size(); i++)
{
index(i);
}
}
else
{
index((uint32_t)m_values.size() - 1);
}
entry = (uint32_t)m_values.size();
}
Value& value = m_values[entry - 1];
if (value.frame != m_frame)
{
value.frame = m_frame;
m_written.push_back(entry - 1);
if (!seeding)
{
if (isColor)
{
value.colorValue = CoreRegistry::getColor(object, propertyKey);
}
else
{
value.doubleValue = CoreRegistry::getDouble(object, propertyKey);
}
}
}
return value;
}
void BlendAccumulator::seedDouble(Core* object, int propertyKey, float value)
{
this->value(object, propertyKey, false, true).doubleValue = value;
}
void BlendAccumulator::seedColor(Core* object, int propertyKey, ColorInt value)
{
this->value(object, propertyKey, true, true).colorValue = value;
}
void BlendAccumulator::applyDouble(
Core* object,
int propertyKey,
float mix,
float value)
{
Value& current = this->value(object, propertyKey, false, false);
if (mix == 1.0f)
{
current.doubleValue = value;
}
else
{
float mixi = 1.0f - mix;
current.doubleValue = current.doubleValue * mixi + value * mix;
}
}
void BlendAccumulator::applyColor(
Core* object,
int propertyKey,
float mix,
ColorInt value)
{
Value& current = this->value(object, propertyKey, true, false);
current.colorValue = mix == 1.0f ? value
: colorLerp(current.colorValue, value, mix);
}
void BlendAccumulator::flush()
{
for (uint32_t written : m_written)
{
const Value& value = m_values[written];
if (value.isColor)
{
CoreRegistry::setColor(value.object, value.propertyKey, value.colorValue);
}
else
{
CoreRegistry::setDouble(
value.object,
value.propertyKey,
value.doubleValue);
}
}
m_written.clear();
// Wrapping back to a frame some value still carries would skip loading
// it, so start the count over with every value marked unwritten.
if (++m_frame == 0)
{
for (Value& value : m_values)
{
value.frame = 0;
}
m_frame = 1;
}
}