blob: f4bf99ba0e6a1e1c8bc001568efb0236ca8d8510 [file] [log] [blame]
// Copyright 2016 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/LengthInterpolationFunctions.h"
#include "core/css/CSSPrimitiveValue.h"
#include "core/css/CSSToLengthConversionData.h"
#include "platform/CalculationValue.h"
namespace blink {
// This class is implemented as a singleton whose instance represents the
// presence of percentages being used in a Length value while nullptr represents
// the absence of any percentages.
class CSSLengthNonInterpolableValue : public NonInterpolableValue {
public:
~CSSLengthNonInterpolableValue() final { NOTREACHED(); }
static PassRefPtr<CSSLengthNonInterpolableValue> create(bool hasPercentage) {
DEFINE_STATIC_REF(CSSLengthNonInterpolableValue, singleton,
adoptRef(new CSSLengthNonInterpolableValue()));
DCHECK(singleton);
return hasPercentage ? singleton : nullptr;
}
static PassRefPtr<CSSLengthNonInterpolableValue> merge(
const NonInterpolableValue* a,
const NonInterpolableValue* b) {
return create(hasPercentage(a) || hasPercentage(b));
}
static bool hasPercentage(const NonInterpolableValue*);
DECLARE_NON_INTERPOLABLE_VALUE_TYPE();
private:
CSSLengthNonInterpolableValue() {}
};
DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSLengthNonInterpolableValue);
DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSLengthNonInterpolableValue);
bool CSSLengthNonInterpolableValue::hasPercentage(
const NonInterpolableValue* nonInterpolableValue) {
DCHECK(isCSSLengthNonInterpolableValue(nonInterpolableValue));
return static_cast<bool>(nonInterpolableValue);
}
std::unique_ptr<InterpolableValue>
LengthInterpolationFunctions::createInterpolablePixels(double pixels) {
std::unique_ptr<InterpolableList> interpolableList =
createNeutralInterpolableValue();
interpolableList->set(CSSPrimitiveValue::UnitTypePixels,
InterpolableNumber::create(pixels));
return std::move(interpolableList);
}
InterpolationValue LengthInterpolationFunctions::createInterpolablePercent(
double percent) {
std::unique_ptr<InterpolableList> interpolableList =
createNeutralInterpolableValue();
interpolableList->set(CSSPrimitiveValue::UnitTypePercentage,
InterpolableNumber::create(percent));
return InterpolationValue(std::move(interpolableList),
CSSLengthNonInterpolableValue::create(true));
}
std::unique_ptr<InterpolableList>
LengthInterpolationFunctions::createNeutralInterpolableValue() {
const size_t length = CSSPrimitiveValue::LengthUnitTypeCount;
std::unique_ptr<InterpolableList> values = InterpolableList::create(length);
for (size_t i = 0; i < length; i++)
values->set(i, InterpolableNumber::create(0));
return values;
}
InterpolationValue LengthInterpolationFunctions::maybeConvertCSSValue(
const CSSValue& value) {
if (!value.isPrimitiveValue())
return nullptr;
const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(value);
if (!primitiveValue.isLength() && !primitiveValue.isPercentage() &&
!primitiveValue.isCalculatedPercentageWithLength())
return nullptr;
CSSLengthArray lengthArray;
primitiveValue.accumulateLengthArray(lengthArray);
std::unique_ptr<InterpolableList> values =
InterpolableList::create(CSSPrimitiveValue::LengthUnitTypeCount);
for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++)
values->set(i, InterpolableNumber::create(lengthArray.values[i]));
bool hasPercentage =
lengthArray.typeFlags.get(CSSPrimitiveValue::UnitTypePercentage);
return InterpolationValue(
std::move(values), CSSLengthNonInterpolableValue::create(hasPercentage));
}
InterpolationValue LengthInterpolationFunctions::maybeConvertLength(
const Length& length,
float zoom) {
if (!length.isSpecified())
return nullptr;
PixelsAndPercent pixelsAndPercent = length.getPixelsAndPercent();
std::unique_ptr<InterpolableList> values = createNeutralInterpolableValue();
values->set(CSSPrimitiveValue::UnitTypePixels,
InterpolableNumber::create(pixelsAndPercent.pixels / zoom));
values->set(CSSPrimitiveValue::UnitTypePercentage,
InterpolableNumber::create(pixelsAndPercent.percent));
return InterpolationValue(
std::move(values),
CSSLengthNonInterpolableValue::create(length.isPercentOrCalc()));
}
PairwiseInterpolationValue LengthInterpolationFunctions::mergeSingles(
InterpolationValue&& start,
InterpolationValue&& end) {
return PairwiseInterpolationValue(
std::move(start.interpolableValue), std::move(end.interpolableValue),
CSSLengthNonInterpolableValue::merge(start.nonInterpolableValue.get(),
end.nonInterpolableValue.get()));
}
bool LengthInterpolationFunctions::nonInterpolableValuesAreCompatible(
const NonInterpolableValue* a,
const NonInterpolableValue* b) {
DCHECK(isCSSLengthNonInterpolableValue(a));
DCHECK(isCSSLengthNonInterpolableValue(b));
return true;
}
void LengthInterpolationFunctions::composite(
std::unique_ptr<InterpolableValue>& underlyingInterpolableValue,
RefPtr<NonInterpolableValue>& underlyingNonInterpolableValue,
double underlyingFraction,
const InterpolableValue& interpolableValue,
const NonInterpolableValue* nonInterpolableValue) {
underlyingInterpolableValue->scaleAndAdd(underlyingFraction,
interpolableValue);
underlyingNonInterpolableValue = CSSLengthNonInterpolableValue::merge(
underlyingNonInterpolableValue.get(), nonInterpolableValue);
}
void LengthInterpolationFunctions::subtractFromOneHundredPercent(
InterpolationValue& result) {
InterpolableList& list = toInterpolableList(*result.interpolableValue);
for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) {
double value = -toInterpolableNumber(*list.get(i)).value();
if (i == CSSPrimitiveValue::UnitTypePercentage)
value += 100;
toInterpolableNumber(*list.getMutable(i)).set(value);
}
result.nonInterpolableValue = CSSLengthNonInterpolableValue::create(true);
}
static double clampToRange(double x, ValueRange range) {
return (range == ValueRangeNonNegative && x < 0) ? 0 : x;
}
Length LengthInterpolationFunctions::createLength(
const InterpolableValue& interpolableValue,
const NonInterpolableValue* nonInterpolableValue,
const CSSToLengthConversionData& conversionData,
ValueRange range) {
const InterpolableList& interpolableList =
toInterpolableList(interpolableValue);
bool hasPercentage =
CSSLengthNonInterpolableValue::hasPercentage(nonInterpolableValue);
double pixels = 0;
double percentage = 0;
for (size_t i = 0; i < CSSPrimitiveValue::LengthUnitTypeCount; i++) {
double value = toInterpolableNumber(*interpolableList.get(i)).value();
if (value == 0)
continue;
if (i == CSSPrimitiveValue::UnitTypePercentage) {
percentage = value;
} else {
CSSPrimitiveValue::UnitType type =
CSSPrimitiveValue::lengthUnitTypeToUnitType(
static_cast<CSSPrimitiveValue::LengthUnitType>(i));
pixels += conversionData.zoomedComputedPixels(value, type);
}
}
if (percentage != 0)
hasPercentage = true;
if (pixels != 0 && hasPercentage)
return Length(
CalculationValue::create(PixelsAndPercent(pixels, percentage), range));
if (hasPercentage)
return Length(clampToRange(percentage, range), Percent);
return Length(
CSSPrimitiveValue::clampToCSSLengthRange(clampToRange(pixels, range)),
Fixed);
}
} // namespace blink