summaryrefslogtreecommitdiffstats
path: root/dom/animation/ComputedTimingFunction.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/animation/ComputedTimingFunction.cpp
parentInitial commit. (diff)
downloadfirefox-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.cpp219
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