Index: Source/core/animation/InvalidatableStyleInterpolation.cpp |
diff --git a/Source/core/animation/InvalidatableStyleInterpolation.cpp b/Source/core/animation/InvalidatableStyleInterpolation.cpp |
index d0f20a686b362d16feadb1ca1e5c59b8d394b7e9..0b787637b2c21f15f926df526a477adaa882ca44 100644 |
--- a/Source/core/animation/InvalidatableStyleInterpolation.cpp |
+++ b/Source/core/animation/InvalidatableStyleInterpolation.cpp |
@@ -97,10 +97,10 @@ bool InvalidatableStyleInterpolation::isCacheValid(const StyleResolverState& sta |
return true; |
} |
-void InvalidatableStyleInterpolation::ensureValidInterpolation(const StyleResolverState& state, const InterpolationValue* underlyingValue) const |
+const InterpolationValue* InvalidatableStyleInterpolation::ensureValidInterpolation(const StyleResolverState& state, const InterpolationValue* underlyingValue) const |
{ |
if (m_cachedConversion && isCacheValid(state, underlyingValue)) |
- return; |
+ return m_cachedValue.get(); |
m_conversionCheckers.clear(); |
if (!maybeCachePairwiseConversion(&state, underlyingValue)) { |
m_cachedConversion = FlipPrimitiveInterpolation::create( |
@@ -108,6 +108,7 @@ void InvalidatableStyleInterpolation::ensureValidInterpolation(const StyleResolv |
convertSingleKeyframe(*m_endKeyframe, state, underlyingValue)); |
} |
m_cachedConversion->interpolateValue(m_currentFraction, m_cachedValue); |
+ return m_cachedValue.get(); |
} |
void InvalidatableStyleInterpolation::setFlagIfInheritUsed(StyleResolverState& state) const |
@@ -120,20 +121,97 @@ void InvalidatableStyleInterpolation::setFlagIfInheritUsed(StyleResolverState& s |
} |
} |
-void InvalidatableStyleInterpolation::apply(StyleResolverState& state) const |
+// Handles memory management of underlying InterpolationValues in applyStack() |
+// Ensures we perform copy on write if we are not the owner of an underlying InterpolationValue. |
+// This functions similar to a DataRef except on OwnPtr'd objects. |
+class UnderlyingValue { |
+ STACK_ALLOCATED(); |
+public: |
+ UnderlyingValue() |
+ : m_owner() |
+ , m_pointer(nullptr) |
+ { } |
+ |
+ void set(const InterpolationValue* interpolationValue) |
+ { |
+ // By clearing m_owner we will perform a copy when attempting to access() |
+ // m_pointer as a mutable reference, thus upholding the const contract for |
+ // this instance of interpolationValue despite the const_cast. |
+ m_owner.clear(); |
+ m_pointer = const_cast<InterpolationValue*>(interpolationValue); |
+ } |
+ void set(PassOwnPtr<InterpolationValue> interpolationValue) |
+ { |
+ m_owner = interpolationValue; |
+ m_pointer = m_owner.get(); |
+ } |
+ InterpolationValue& access() |
+ { |
+ ASSERT(m_pointer); |
+ if (!m_owner) |
+ set(m_pointer->clone()); |
+ return *m_pointer; |
+ } |
+ const InterpolationValue* get() const { return m_pointer; } |
+ operator bool() const { return m_pointer; } |
+ const InterpolationValue* operator->() const |
+ { |
+ ASSERT(m_pointer); |
+ return m_pointer; |
+ } |
+ |
+private: |
+ OwnPtr<InterpolationValue> m_owner; |
+ InterpolationValue* m_pointer; |
+}; |
+ |
+void InvalidatableStyleInterpolation::applyStack(const ActiveInterpolations& interpolations, StyleResolverState& state) |
{ |
- OwnPtr<InterpolationValue> underlyingValue = dependsOnUnderlyingValue() ? maybeConvertUnderlyingValue(state) : nullptr; |
- ensureValidInterpolation(state, underlyingValue.get()); |
- if (!m_cachedValue) |
- return; |
- const InterpolableValue* appliedInterpolableValue = &m_cachedValue->interpolableValue(); |
- if (underlyingValue && m_cachedValue->type() == underlyingValue->type()) { |
- double underlyingFraction = m_cachedConversion->interpolateUnderlyingFraction(m_startKeyframe->underlyingFraction(), m_endKeyframe->underlyingFraction(), m_currentFraction); |
- underlyingValue->interpolableValue().scaleAndAdd(underlyingFraction, m_cachedValue->interpolableValue()); |
- appliedInterpolableValue = &underlyingValue->interpolableValue(); |
+ ASSERT(!interpolations.isEmpty()); |
+ size_t startingIndex = 0; |
+ |
+ // Compute the underlying value to composite onto. |
+ UnderlyingValue underlyingValue; |
+ const InvalidatableStyleInterpolation& firstInterpolation = toInvalidatableStyleInterpolation(*interpolations.at(startingIndex)); |
+ if (firstInterpolation.dependsOnUnderlyingValue()) { |
+ underlyingValue.set(firstInterpolation.maybeConvertUnderlyingValue(state)); |
+ } else { |
+ const InterpolationValue* firstValue = firstInterpolation.ensureValidInterpolation(state, nullptr); |
+ // Fast path for replace interpolations that are the only one to apply. |
+ if (interpolations.size() == 1) { |
+ if (firstValue) { |
+ firstInterpolation.setFlagIfInheritUsed(state); |
+ firstValue->type().apply(firstValue->interpolableValue(), firstValue->nonInterpolableValue(), state); |
+ } |
+ return; |
+ } |
+ underlyingValue.set(firstValue); |
+ startingIndex++; |
+ } |
+ |
+ // Composite interpolations onto the underlying value. |
+ bool shouldApply = false; |
+ for (size_t i = startingIndex; i < interpolations.size(); i++) { |
+ const InvalidatableStyleInterpolation& currentInterpolation = toInvalidatableStyleInterpolation(*interpolations.at(i)); |
+ ASSERT(currentInterpolation.dependsOnUnderlyingValue()); |
+ const InterpolationValue* currentValue = currentInterpolation.ensureValidInterpolation(state, underlyingValue.get()); |
+ if (!currentValue) |
+ continue; |
+ shouldApply = true; |
+ currentInterpolation.setFlagIfInheritUsed(state); |
+ if (!underlyingValue || underlyingValue->type() != currentValue->type()) { |
+ underlyingValue.set(currentValue); |
+ } else { |
+ double underlyingFraction = currentInterpolation.m_cachedConversion->interpolateUnderlyingFraction( |
+ currentInterpolation.m_startKeyframe->underlyingFraction(), |
+ currentInterpolation.m_endKeyframe->underlyingFraction(), |
+ currentInterpolation.m_currentFraction); |
+ underlyingValue.access().interpolableValue().scaleAndAdd(underlyingFraction, currentInterpolation.m_cachedValue->interpolableValue()); |
+ } |
} |
- m_cachedValue->type().apply(*appliedInterpolableValue, m_cachedValue->nonInterpolableValue(), state); |
- setFlagIfInheritUsed(state); |
+ |
+ if (shouldApply && underlyingValue) |
+ underlyingValue->type().apply(underlyingValue->interpolableValue(), underlyingValue->nonInterpolableValue(), state); |
} |
} // namespace blink |