blob: d150cd0be5539619024540f246c3ce1ee1be22fa [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/animation/ListInterpolationFunctions.h"
#include "core/animation/UnderlyingValue.h"
#include "core/css/CSSValueList.h"
#include "wtf/MathExtras.h"
namespace blink {
DEFINE_NON_INTERPOLABLE_VALUE_TYPE(NonInterpolableList);
bool ListInterpolationFunctions::equalValues(const InterpolationComponent& a, const InterpolationComponent& b, EqualNonInterpolableValuesCallback equalNonInterpolableValues)
{
const InterpolableList& interpolableListA = toInterpolableList(*a.interpolableValue);
const InterpolableList& interpolableListB = toInterpolableList(*b.interpolableValue);
if (interpolableListA.length() != interpolableListB.length())
return false;
size_t length = interpolableListA.length();
if (length == 0)
return true;
const NonInterpolableList& nonInterpolableListA = toNonInterpolableList(*a.nonInterpolableValue);
const NonInterpolableList& nonInterpolableListB = toNonInterpolableList(*b.nonInterpolableValue);
for (size_t i = 0; i < length; i++) {
if (!equalNonInterpolableValues(nonInterpolableListA.get(i), nonInterpolableListB.get(i)))
return false;
}
return true;
}
PairwiseInterpolationComponent ListInterpolationFunctions::mergeSingleConversions(InterpolationComponent& start, InterpolationComponent& end, MergeSingleItemConversionsCallback mergeSingleItemConversions)
{
size_t startLength = toInterpolableList(*start.interpolableValue).length();
size_t endLength = toInterpolableList(*end.interpolableValue).length();
if (startLength == 0 && endLength == 0) {
return PairwiseInterpolationComponent(
start.interpolableValue.release(),
end.interpolableValue.release(),
nullptr);
}
if (startLength == 0) {
OwnPtr<InterpolableValue> startInterpolableValue = end.interpolableValue->cloneAndZero();
return PairwiseInterpolationComponent(
startInterpolableValue.release(),
end.interpolableValue.release(),
end.nonInterpolableValue.release());
}
if (endLength == 0) {
OwnPtr<InterpolableValue> endInterpolableValue = start.interpolableValue->cloneAndZero();
return PairwiseInterpolationComponent(
start.interpolableValue.release(),
endInterpolableValue.release(),
start.nonInterpolableValue.release());
}
size_t finalLength = lowestCommonMultiple(startLength, endLength);
OwnPtr<InterpolableList> resultStartInterpolableList = InterpolableList::create(finalLength);
OwnPtr<InterpolableList> resultEndInterpolableList = InterpolableList::create(finalLength);
Vector<RefPtr<NonInterpolableValue>> resultNonInterpolableValues(finalLength);
InterpolableList& startInterpolableList = toInterpolableList(*start.interpolableValue);
InterpolableList& endInterpolableList = toInterpolableList(*end.interpolableValue);
NonInterpolableList& startNonInterpolableList = toNonInterpolableList(*start.nonInterpolableValue);
NonInterpolableList& endNonInterpolableList = toNonInterpolableList(*end.nonInterpolableValue);
for (size_t i = 0; i < finalLength; i++) {
InterpolationComponent startComponent(startInterpolableList.get(i % startLength)->clone(), startNonInterpolableList.get(i % startLength));
InterpolationComponent endComponent(endInterpolableList.get(i % endLength)->clone(), endNonInterpolableList.get(i % endLength));
PairwiseInterpolationComponent resultComponent = mergeSingleItemConversions(startComponent, endComponent);
if (!resultComponent)
return nullptr;
resultStartInterpolableList->set(i, resultComponent.startInterpolableValue.release());
resultEndInterpolableList->set(i, resultComponent.endInterpolableValue.release());
resultNonInterpolableValues[i] = resultComponent.nonInterpolableValue.release();
}
return PairwiseInterpolationComponent(
resultStartInterpolableList.release(),
resultEndInterpolableList.release(),
NonInterpolableList::create(resultNonInterpolableValues));
}
static void repeatToLength(InterpolationComponent& value, size_t length)
{
InterpolableList& interpolableList = toInterpolableList(*value.interpolableValue);
NonInterpolableList& nonInterpolableList = toNonInterpolableList(*value.nonInterpolableValue);
size_t currentLength = interpolableList.length();
ASSERT(currentLength > 0);
if (currentLength == length)
return;
ASSERT(currentLength < length);
OwnPtr<InterpolableList> newInterpolableList = InterpolableList::create(length);
Vector<RefPtr<NonInterpolableValue>> newNonInterpolableValues(length);
for (size_t i = length; i-- > 0;) {
newInterpolableList->set(i, i < currentLength ? interpolableList.getMutable(i).release() : interpolableList.get(i % currentLength)->clone());
newNonInterpolableValues[i] = nonInterpolableList.get(i % currentLength);
}
value.interpolableValue = newInterpolableList.release();
value.nonInterpolableValue = NonInterpolableList::create(newNonInterpolableValues);
}
static bool nonInterpolableListsAreCompatible(const NonInterpolableList& a, const NonInterpolableList& b, size_t length, ListInterpolationFunctions::NonInterpolableValuesAreCompatibleCallback nonInterpolableValuesAreCompatible)
{
for (size_t i = 0; i < length; i++) {
if (!nonInterpolableValuesAreCompatible(a.get(i % a.length()), b.get(i % b.length())))
return false;
}
return true;
}
void ListInterpolationFunctions::composite(UnderlyingValue& underlyingValue, double underlyingFraction, const InterpolationValue& value, NonInterpolableValuesAreCompatibleCallback nonInterpolableValuesAreCompatible, CompositeItemCallback compositeItem)
{
size_t underlyingLength = toInterpolableList(underlyingValue->interpolableValue()).length();
if (underlyingLength == 0) {
ASSERT(!underlyingValue->nonInterpolableValue());
underlyingValue.set(&value);
return;
}
const InterpolableList& interpolableList = toInterpolableList(value.interpolableValue());
size_t valueLength = interpolableList.length();
if (valueLength == 0) {
ASSERT(!value.nonInterpolableValue());
underlyingValue.mutableComponent().interpolableValue->scale(underlyingFraction);
return;
}
const NonInterpolableList& nonInterpolableList = toNonInterpolableList(*value.nonInterpolableValue());
size_t newLength = lowestCommonMultiple(underlyingLength, valueLength);
if (!nonInterpolableListsAreCompatible(toNonInterpolableList(*underlyingValue->nonInterpolableValue()), nonInterpolableList, newLength, nonInterpolableValuesAreCompatible)) {
underlyingValue.set(&value);
return;
}
InterpolationComponent& underlyingComponent = underlyingValue.mutableComponent();
if (underlyingLength < newLength)
repeatToLength(underlyingComponent, newLength);
InterpolableList& underlyingInterpolableList = toInterpolableList(*underlyingComponent.interpolableValue);
NonInterpolableList& underlyingNonInterpolableList = toNonInterpolableList(*underlyingComponent.nonInterpolableValue);
for (size_t i = 0; i < newLength; i++) {
compositeItem(
underlyingInterpolableList.getMutable(i),
underlyingNonInterpolableList.getMutable(i),
underlyingFraction,
*interpolableList.get(i % valueLength),
nonInterpolableList.get(i % valueLength));
}
}
} // namespace blink