blob: c349b826bc40b4ea80c6bdf6e130c1beec578d2c [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/CSSScaleInterpolationType.h"
#include "core/css/CSSPrimitiveValue.h"
#include "core/css/CSSValueList.h"
#include "core/css/resolver/StyleResolverState.h"
#include "wtf/PtrUtil.h"
#include <memory>
namespace blink {
namespace {
struct Scale {
Scale(double x, double y, double z) { init(x, y, z); }
explicit Scale(const ScaleTransformOperation* scale) {
if (scale)
init(scale->x(), scale->y(), scale->z());
else
init(1, 1, 1);
}
explicit Scale(const InterpolableValue& value) {
const InterpolableList& list = toInterpolableList(value);
init(toInterpolableNumber(*list.get(0)).value(),
toInterpolableNumber(*list.get(1)).value(),
toInterpolableNumber(*list.get(2)).value());
}
void init(double x, double y, double z) {
array[0] = x;
array[1] = y;
array[2] = z;
}
std::unique_ptr<InterpolableValue> createInterpolableValue() const {
std::unique_ptr<InterpolableList> result = InterpolableList::create(3);
for (size_t i = 0; i < 3; i++)
result->set(i, InterpolableNumber::create(array[i]));
return std::move(result);
}
bool operator==(const Scale& other) const {
for (size_t i = 0; i < 3; i++) {
if (array[i] != other.array[i])
return false;
}
return true;
}
double array[3];
};
class ParentScaleChecker : public InterpolationType::ConversionChecker {
public:
static std::unique_ptr<ParentScaleChecker> create(const Scale& scale) {
return wrapUnique(new ParentScaleChecker(scale));
}
private:
ParentScaleChecker(const Scale& scale) : m_scale(scale) {}
bool isValid(const InterpolationEnvironment& environment,
const InterpolationValue&) const final {
return m_scale == Scale(environment.state().parentStyle()->scale());
}
const Scale m_scale;
};
} // namespace
class CSSScaleNonInterpolableValue : public NonInterpolableValue {
public:
~CSSScaleNonInterpolableValue() final {}
static PassRefPtr<CSSScaleNonInterpolableValue> create(const Scale& scale,
bool isAdditive) {
return adoptRef(
new CSSScaleNonInterpolableValue(scale, scale, isAdditive, isAdditive));
}
static PassRefPtr<CSSScaleNonInterpolableValue> merge(
const CSSScaleNonInterpolableValue& start,
const CSSScaleNonInterpolableValue& end) {
return adoptRef(new CSSScaleNonInterpolableValue(start.start(), end.end(),
start.isStartAdditive(),
end.isEndAdditive()));
}
const Scale& start() const { return m_start; }
const Scale& end() const { return m_end; }
bool isStartAdditive() const { return m_isStartAdditive; }
bool isEndAdditive() const { return m_isEndAdditive; }
DECLARE_NON_INTERPOLABLE_VALUE_TYPE();
private:
CSSScaleNonInterpolableValue(const Scale& start,
const Scale& end,
bool isStartAdditive,
bool isEndAdditive)
: m_start(start),
m_end(end),
m_isStartAdditive(isStartAdditive),
m_isEndAdditive(isEndAdditive) {}
const Scale m_start;
const Scale m_end;
const bool m_isStartAdditive;
const bool m_isEndAdditive;
};
DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSScaleNonInterpolableValue);
DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSScaleNonInterpolableValue);
InterpolationValue CSSScaleInterpolationType::maybeConvertNeutral(
const InterpolationValue&,
ConversionCheckers&) const {
return InterpolationValue(Scale(1, 1, 1).createInterpolableValue());
}
InterpolationValue CSSScaleInterpolationType::maybeConvertInitial(
const StyleResolverState&,
ConversionCheckers&) const {
return InterpolationValue(Scale(1, 1, 1).createInterpolableValue());
}
InterpolationValue CSSScaleInterpolationType::maybeConvertInherit(
const StyleResolverState& state,
ConversionCheckers& conversionCheckers) const {
Scale parentScale(state.parentStyle()->scale());
conversionCheckers.append(ParentScaleChecker::create(parentScale));
return InterpolationValue(parentScale.createInterpolableValue());
}
InterpolationValue CSSScaleInterpolationType::maybeConvertValue(
const CSSValue& value,
const StyleResolverState&,
ConversionCheckers&) const {
Scale scale(1, 1, 1);
if (!value.isBaseValueList())
return nullptr;
const CSSValueList& list = toCSSValueList(value);
if (list.length() < 1 || list.length() > 3)
return nullptr;
for (size_t i = 0; i < list.length(); i++) {
const CSSValue& item = list.item(i);
if (!item.isPrimitiveValue() || !toCSSPrimitiveValue(item).isNumber())
return nullptr;
scale.array[i] = toCSSPrimitiveValue(item).getDoubleValue();
}
if (list.length() == 1)
scale.array[1] = scale.array[0];
return InterpolationValue(scale.createInterpolableValue());
}
InterpolationValue CSSScaleInterpolationType::maybeConvertSingle(
const PropertySpecificKeyframe& keyframe,
const InterpolationEnvironment& environment,
const InterpolationValue& underlying,
ConversionCheckers& conversionCheckers) const {
InterpolationValue result = CSSInterpolationType::maybeConvertSingle(
keyframe, environment, underlying, conversionCheckers);
if (!result)
return nullptr;
result.nonInterpolableValue = CSSScaleNonInterpolableValue::create(
Scale(*result.interpolableValue),
keyframe.composite() != EffectModel::CompositeReplace);
return result;
}
PairwiseInterpolationValue CSSScaleInterpolationType::maybeMergeSingles(
InterpolationValue&& start,
InterpolationValue&& end) const {
return PairwiseInterpolationValue(
std::move(start.interpolableValue), std::move(end.interpolableValue),
CSSScaleNonInterpolableValue::merge(
toCSSScaleNonInterpolableValue(*start.nonInterpolableValue),
toCSSScaleNonInterpolableValue(*end.nonInterpolableValue)));
}
InterpolationValue CSSScaleInterpolationType::maybeConvertUnderlyingValue(
const InterpolationEnvironment& environment) const {
return InterpolationValue(
Scale(environment.state().style()->scale()).createInterpolableValue());
}
void CSSScaleInterpolationType::composite(
UnderlyingValueOwner& underlyingValueOwner,
double underlyingFraction,
const InterpolationValue& value,
double interpolationFraction) const {
const CSSScaleNonInterpolableValue& metadata =
toCSSScaleNonInterpolableValue(*value.nonInterpolableValue);
DCHECK(metadata.isStartAdditive() || metadata.isEndAdditive());
InterpolableList& underlyingList = toInterpolableList(
*underlyingValueOwner.mutableValue().interpolableValue);
for (size_t i = 0; i < 3; i++) {
InterpolableNumber& underlying =
toInterpolableNumber(*underlyingList.getMutable(i));
double start = metadata.start().array[i] *
(metadata.isStartAdditive() ? underlying.value() : 1);
double end = metadata.end().array[i] *
(metadata.isEndAdditive() ? underlying.value() : 1);
underlying.set(blend(start, end, interpolationFraction));
}
}
void CSSScaleInterpolationType::apply(
const InterpolableValue& interpolableValue,
const NonInterpolableValue*,
InterpolationEnvironment& environment) const {
Scale scale(interpolableValue);
environment.state().style()->setScale(ScaleTransformOperation::create(
scale.array[0], scale.array[1], scale.array[2],
TransformOperation::Scale3D));
}
} // namespace blink