diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/animation/ComputedTimingFunction.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/animation/ComputedTimingFunction.cpp')
-rw-r--r-- | dom/animation/ComputedTimingFunction.cpp | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/dom/animation/ComputedTimingFunction.cpp b/dom/animation/ComputedTimingFunction.cpp new file mode 100644 index 0000000000..17b8de323d --- /dev/null +++ b/dom/animation/ComputedTimingFunction.cpp @@ -0,0 +1,219 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ComputedTimingFunction.h" +#include "mozilla/ServoBindings.h" +#include "nsAlgorithm.h" // For clamped() + +namespace mozilla { + +void ComputedTimingFunction::Init(const nsTimingFunction& aFunction) { + const StyleComputedTimingFunction& timing = aFunction.mTiming; + switch (timing.tag) { + case StyleComputedTimingFunction::Tag::Keyword: { + mType = static_cast<Type>(static_cast<uint8_t>(timing.keyword._0)); + + static_assert( + static_cast<uint8_t>(StyleTimingKeyword::Linear) == 0 && + static_cast<uint8_t>(StyleTimingKeyword::Ease) == 1 && + static_cast<uint8_t>(StyleTimingKeyword::EaseIn) == 2 && + static_cast<uint8_t>(StyleTimingKeyword::EaseOut) == 3 && + static_cast<uint8_t>(StyleTimingKeyword::EaseInOut) == 4, + "transition timing function constants not as expected"); + + static const float timingFunctionValues[5][4] = { + {0.00f, 0.00f, 1.00f, 1.00f}, // linear + {0.25f, 0.10f, 0.25f, 1.00f}, // ease + {0.42f, 0.00f, 1.00f, 1.00f}, // ease-in + {0.00f, 0.00f, 0.58f, 1.00f}, // ease-out + {0.42f, 0.00f, 0.58f, 1.00f} // ease-in-out + }; + const float(&values)[4] = timingFunctionValues[uint8_t(mType)]; + mTimingFunction.Init(values[0], values[1], values[2], values[3]); + break; + } + case StyleComputedTimingFunction::Tag::CubicBezier: + mType = Type::CubicBezier; + mTimingFunction.Init(timing.cubic_bezier.x1, timing.cubic_bezier.y1, + timing.cubic_bezier.x2, timing.cubic_bezier.y2); + break; + case StyleComputedTimingFunction::Tag::Steps: + mType = Type::Step; + mSteps.mSteps = static_cast<uint32_t>(timing.steps._0); + mSteps.mPos = timing.steps._1; + break; + } +} + +static inline double StepTiming( + const ComputedTimingFunction::StepFunc& aStepFunc, double aPortion, + ComputedTimingFunction::BeforeFlag aBeforeFlag) { + // Use the algorithm defined in the spec: + // https://drafts.csswg.org/css-easing-1/#step-timing-function-algo + + // Calculate current step. + int32_t currentStep = floor(aPortion * aStepFunc.mSteps); + + // Increment current step if it is jump-start or start. + if (aStepFunc.mPos == StyleStepPosition::Start || + aStepFunc.mPos == StyleStepPosition::JumpStart || + aStepFunc.mPos == StyleStepPosition::JumpBoth) { + ++currentStep; + } + + // If the "before flag" is set and we are at a transition point, + // drop back a step + if (aBeforeFlag == ComputedTimingFunction::BeforeFlag::Set && + fmod(aPortion * aStepFunc.mSteps, 1) == 0) { + --currentStep; + } + + // We should not produce a result outside [0, 1] unless we have an + // input outside that range. This takes care of steps that would otherwise + // occur at boundaries. + if (aPortion >= 0.0 && currentStep < 0) { + currentStep = 0; + } + + int32_t jumps = aStepFunc.mSteps; + if (aStepFunc.mPos == StyleStepPosition::JumpBoth) { + ++jumps; + } else if (aStepFunc.mPos == StyleStepPosition::JumpNone) { + --jumps; + } + + if (aPortion <= 1.0 && currentStep > jumps) { + currentStep = jumps; + } + + // Convert to the output progress value. + MOZ_ASSERT(jumps > 0, "`jumps` should be a positive integer"); + return double(currentStep) / double(jumps); +} + +double ComputedTimingFunction::GetValue( + double aPortion, ComputedTimingFunction::BeforeFlag aBeforeFlag) const { + if (HasSpline()) { + // Check for a linear curve. + // (GetSplineValue(), below, also checks this but doesn't work when + // aPortion is outside the range [0.0, 1.0]). + if (mTimingFunction.X1() == mTimingFunction.Y1() && + mTimingFunction.X2() == mTimingFunction.Y2()) { + return aPortion; + } + + // Ensure that we return 0 or 1 on both edges. + if (aPortion == 0.0) { + return 0.0; + } + if (aPortion == 1.0) { + return 1.0; + } + + // For negative values, try to extrapolate with tangent (p1 - p0) or, + // if p1 is coincident with p0, with (p2 - p0). + if (aPortion < 0.0) { + if (mTimingFunction.X1() > 0.0) { + return aPortion * mTimingFunction.Y1() / mTimingFunction.X1(); + } else if (mTimingFunction.Y1() == 0 && mTimingFunction.X2() > 0.0) { + return aPortion * mTimingFunction.Y2() / mTimingFunction.X2(); + } + // If we can't calculate a sensible tangent, don't extrapolate at all. + return 0.0; + } + + // For values greater than 1, try to extrapolate with tangent (p2 - p3) or, + // if p2 is coincident with p3, with (p1 - p3). + if (aPortion > 1.0) { + if (mTimingFunction.X2() < 1.0) { + return 1.0 + (aPortion - 1.0) * (mTimingFunction.Y2() - 1) / + (mTimingFunction.X2() - 1); + } else if (mTimingFunction.Y2() == 1 && mTimingFunction.X1() < 1.0) { + return 1.0 + (aPortion - 1.0) * (mTimingFunction.Y1() - 1) / + (mTimingFunction.X1() - 1); + } + // If we can't calculate a sensible tangent, don't extrapolate at all. + return 1.0; + } + + return mTimingFunction.GetSplineValue(aPortion); + } + + return StepTiming(mSteps, aPortion, aBeforeFlag); +} + +int32_t ComputedTimingFunction::Compare( + const ComputedTimingFunction& aRhs) const { + if (mType != aRhs.mType) { + return int32_t(mType) - int32_t(aRhs.mType); + } + + if (mType == Type::CubicBezier) { + int32_t order = mTimingFunction.Compare(aRhs.mTimingFunction); + if (order != 0) { + return order; + } + } else if (mType == Type::Step) { + if (mSteps.mPos != aRhs.mSteps.mPos) { + return int32_t(mSteps.mPos) - int32_t(aRhs.mSteps.mPos); + } else if (mSteps.mSteps != aRhs.mSteps.mSteps) { + return int32_t(mSteps.mSteps) - int32_t(aRhs.mSteps.mSteps); + } + } + + return 0; +} + +void ComputedTimingFunction::AppendToString(nsACString& aResult) const { + nsTimingFunction timing; + switch (mType) { + case Type::CubicBezier: + timing.mTiming = StyleComputedTimingFunction::CubicBezier( + mTimingFunction.X1(), mTimingFunction.Y1(), mTimingFunction.X2(), + mTimingFunction.Y2()); + break; + case Type::Step: + timing.mTiming = + StyleComputedTimingFunction::Steps(mSteps.mSteps, mSteps.mPos); + break; + case Type::Linear: + case Type::Ease: + case Type::EaseIn: + case Type::EaseOut: + case Type::EaseInOut: + timing.mTiming = StyleComputedTimingFunction::Keyword( + static_cast<StyleTimingKeyword>(mType)); + break; + default: + MOZ_ASSERT_UNREACHABLE("Unsupported timing type"); + } + Servo_SerializeEasing(&timing, &aResult); +} + +/* static */ +int32_t ComputedTimingFunction::Compare( + const Maybe<ComputedTimingFunction>& aLhs, + const Maybe<ComputedTimingFunction>& aRhs) { + // We can't use |operator<| for const Maybe<>& here because + // 'ease' is prior to 'linear' which is represented by Nothing(). + // So we have to convert Nothing() as 'linear' and check it first. + Type lhsType = aLhs.isNothing() ? Type::Linear : aLhs->GetType(); + Type rhsType = aRhs.isNothing() ? Type::Linear : aRhs->GetType(); + + if (lhsType != rhsType) { + return int32_t(lhsType) - int32_t(rhsType); + } + + // Both of them are Nothing(). + if (lhsType == Type::Linear) { + return 0; + } + + // Other types. + return aLhs->Compare(aRhs.value()); +} + +} // namespace mozilla |