1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "SVGAnimatedLength.h"
9 #include "mozAutoDocUpdate.h"
10 #include "mozilla/ArrayUtils.h"
11 #include "mozilla/GeckoBindings.h"
12 #include "mozilla/Maybe.h"
13 #include "mozilla/PresShell.h"
14 #include "mozilla/SMILValue.h"
15 #include "mozilla/StaticPresData.h"
16 #include "nsPresContextInlines.h"
17 #include "mozilla/SVGIntegrationUtils.h"
18 #include "mozilla/dom/SVGViewportElement.h"
19 #include "DOMSVGAnimatedLength.h"
20 #include "DOMSVGLength.h"
21 #include "LayoutLogging.h"
22 #include "nsContentUtils.h"
24 #include "nsLayoutUtils.h"
25 #include "nsTextFormatter.h"
26 #include "SVGLengthSMILType.h"
27 #include "SVGAttrTearoffTable.h"
28 #include "SVGGeometryProperty.h"
30 using namespace mozilla::dom
;
34 //----------------------------------------------------------------------
35 // Helper class: AutoChangeLengthNotifier
36 // Stack-based helper class to pair calls to WillChangeLength and
38 class MOZ_RAII AutoChangeLengthNotifier
{
40 AutoChangeLengthNotifier(SVGAnimatedLength
* aLength
, SVGElement
* aSVGElement
,
41 bool aDoSetAttr
= true)
42 : mLength(aLength
), mSVGElement(aSVGElement
), mDoSetAttr(aDoSetAttr
) {
43 MOZ_ASSERT(mLength
, "Expecting non-null length");
44 MOZ_ASSERT(mSVGElement
, "Expecting non-null element");
47 mUpdateBatch
.emplace(aSVGElement
->GetComposedDoc(), true);
49 mSVGElement
->WillChangeLength(mLength
->mAttrEnum
, mUpdateBatch
.ref());
53 ~AutoChangeLengthNotifier() {
55 mSVGElement
->DidChangeLength(mLength
->mAttrEnum
, mEmptyOrOldValue
,
58 if (mLength
->mIsAnimated
) {
59 mSVGElement
->AnimationNeedsResample();
64 SVGAnimatedLength
* const mLength
;
65 SVGElement
* const mSVGElement
;
66 Maybe
<mozAutoDocUpdate
> mUpdateBatch
;
67 nsAttrValue mEmptyOrOldValue
;
71 MOZ_CONSTINIT
static SVGAttrTearoffTable
<SVGAnimatedLength
,
73 sSVGAnimatedLengthTearoffTable
;
75 /* Helper functions */
77 static void GetValueString(nsAString
& aValueAsString
, float aValue
,
79 nsTextFormatter::ssprintf(aValueAsString
, u
"%g", (double)aValue
);
81 nsAutoString unitString
;
82 SVGLength::GetUnitString(unitString
, aUnitType
);
83 aValueAsString
.Append(unitString
);
86 static bool GetValueFromString(const nsAString
& aString
, float& aValue
,
87 uint16_t* aUnitType
) {
89 auto token
= SVGContentUtils::GetAndEnsureOneToken(aString
, success
);
95 nsAString::const_iterator iter
, end
;
96 token
.BeginReading(iter
);
97 token
.EndReading(end
);
99 if (!SVGContentUtils::ParseNumber(iter
, end
, aValue
)) {
102 const nsAString
& units
= Substring(iter
, end
);
103 *aUnitType
= SVGLength::GetUnitTypeForString(units
);
104 return *aUnitType
!= SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN
;
107 static float FixAxisLength(float aLength
) {
108 if (aLength
== 0.0f
) {
109 LAYOUT_WARNING("zero axis length");
115 GeckoFontMetrics
UserSpaceMetrics::DefaultFontMetrics() {
116 return {StyleLength::FromPixels(16.0f
),
117 StyleLength::FromPixels(-1),
118 StyleLength::FromPixels(-1),
119 StyleLength::FromPixels(-1),
120 StyleLength::FromPixels(-1),
121 StyleLength::FromPixels(16.0f
),
126 GeckoFontMetrics
UserSpaceMetrics::GetFontMetrics(const Element
* aElement
) {
127 GeckoFontMetrics metrics
= DefaultFontMetrics();
129 aElement
? nsContentUtils::GetContextForContent(aElement
) : nullptr;
131 SVGGeometryProperty::DoForComputedStyle(
132 aElement
, [&](const ComputedStyle
* style
) {
133 metrics
= Gecko_GetFontMetrics(
134 presContext
, WritingMode(style
).IsVertical(), style
->StyleFont(),
135 style
->StyleFont()->mFont
.size
,
136 /* aUseUserFontSet = */ true,
137 /* aRetrieveMathScales */ false);
143 WritingMode
UserSpaceMetrics::GetWritingMode(const Element
* aElement
) {
144 WritingMode writingMode
;
145 SVGGeometryProperty::DoForComputedStyle(
147 [&](const ComputedStyle
* style
) { writingMode
= WritingMode(style
); });
151 float UserSpaceMetrics::GetZoom(const Element
* aElement
) {
153 SVGGeometryProperty::DoForComputedStyle(
154 aElement
, [&](const ComputedStyle
* style
) {
155 zoom
= style
->EffectiveZoom().ToFloat();
160 float UserSpaceMetrics::GetExLength(Type aType
) const {
161 return GetFontMetricsForType(aType
).mXSize
.ToCSSPixels();
164 float UserSpaceMetrics::GetChSize(Type aType
) const {
165 auto metrics
= GetFontMetricsForType(aType
);
166 if (metrics
.mChSize
.ToCSSPixels() > 0.0) {
167 return metrics
.mChSize
.ToCSSPixels();
169 auto emLength
= GetEmLength(aType
);
170 WritingMode writingMode
= GetWritingModeForType(aType
);
171 return writingMode
.IsVertical() && !writingMode
.IsSideways()
176 float UserSpaceMetrics::GetIcWidth(Type aType
) const {
177 auto metrics
= GetFontMetricsForType(aType
);
178 if (metrics
.mIcWidth
.ToCSSPixels() > 0.0) {
179 return metrics
.mIcWidth
.ToCSSPixels();
181 return GetEmLength(aType
);
184 float UserSpaceMetrics::GetCapHeight(Type aType
) const {
185 auto metrics
= GetFontMetricsForType(aType
);
186 if (metrics
.mCapHeight
.ToCSSPixels() > 0.0) {
187 return metrics
.mCapHeight
.ToCSSPixels();
189 return GetEmLength(aType
);
192 CSSSize
UserSpaceMetrics::GetCSSViewportSizeFromContext(
193 const nsPresContext
* aContext
) {
194 return CSSPixel::FromAppUnits(aContext
->GetSizeForViewportUnits());
197 SVGElementMetrics::SVGElementMetrics(const SVGElement
* aSVGElement
,
198 const SVGViewportElement
* aCtx
)
199 : mSVGElement(aSVGElement
), mCtx(aCtx
) {}
201 const Element
* SVGElementMetrics::GetElementForType(Type aType
) const {
206 return mSVGElement
? mSVGElement
->OwnerDoc()->GetRootElement() : nullptr;
208 MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
213 GeckoFontMetrics
SVGElementMetrics::GetFontMetricsForType(Type aType
) const {
214 return GetFontMetrics(GetElementForType(aType
));
217 WritingMode
SVGElementMetrics::GetWritingModeForType(Type aType
) const {
218 return GetWritingMode(GetElementForType(aType
));
221 float SVGElementMetrics::GetZoom() const {
222 return UserSpaceMetrics::GetZoom(mSVGElement
);
225 float SVGElementMetrics::GetRootZoom() const {
226 return UserSpaceMetrics::GetZoom(
227 mSVGElement
? mSVGElement
->OwnerDoc()->GetRootElement() : nullptr);
230 float SVGElementMetrics::GetAxisLength(uint8_t aCtxType
) const {
235 return FixAxisLength(mCtx
->GetLength(aCtxType
));
238 CSSSize
SVGElementMetrics::GetCSSViewportSize() const {
242 nsPresContext
* context
= nsContentUtils::GetContextForContent(mSVGElement
);
246 return GetCSSViewportSizeFromContext(context
);
249 float SVGElementMetrics::GetLineHeight(Type aType
) const {
250 return SVGContentUtils::GetLineHeight(GetElementForType(aType
));
253 bool SVGElementMetrics::EnsureCtx() const {
254 if (!mCtx
&& mSVGElement
) {
255 mCtx
= mSVGElement
->GetCtx();
256 if (!mCtx
&& mSVGElement
->IsSVGElement(nsGkAtoms::svg
)) {
257 const auto* e
= static_cast<const SVGViewportElement
*>(mSVGElement
);
260 // mSVGElement must be the outer svg element
265 return mCtx
!= nullptr;
268 NonSVGFrameUserSpaceMetrics::NonSVGFrameUserSpaceMetrics(nsIFrame
* aFrame
)
270 MOZ_ASSERT(mFrame
, "Need a frame");
273 float NonSVGFrameUserSpaceMetrics::GetEmLength(Type aType
) const {
276 return SVGContentUtils::GetFontSize(mFrame
);
278 return SVGContentUtils::GetFontSize(
279 mFrame
->PresContext()->Document()->GetRootElement());
281 MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
286 GeckoFontMetrics
NonSVGFrameUserSpaceMetrics::GetFontMetricsForType(
290 return Gecko_GetFontMetrics(
291 mFrame
->PresContext(), mFrame
->GetWritingMode().IsVertical(),
292 mFrame
->StyleFont(), mFrame
->StyleFont()->mFont
.size
,
293 /* aUseUserFontSet = */ true,
294 /* aRetrieveMathScales */ false);
296 return GetFontMetrics(
297 mFrame
->PresContext()->Document()->GetRootElement());
299 MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
300 return DefaultFontMetrics();
304 WritingMode
NonSVGFrameUserSpaceMetrics::GetWritingModeForType(
308 return mFrame
->GetWritingMode();
310 return GetWritingMode(
311 mFrame
->PresContext()->Document()->GetRootElement());
313 MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
314 return WritingMode();
318 float NonSVGFrameUserSpaceMetrics::GetZoom() const {
319 return mFrame
->Style()->EffectiveZoom().ToFloat();
322 float NonSVGFrameUserSpaceMetrics::GetRootZoom() const {
323 return mFrame
->PresContext()
325 ->GetRootElementStyleFrame()
331 gfx::Size
NonSVGFrameUserSpaceMetrics::GetSize() const {
332 return SVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(mFrame
);
335 CSSSize
NonSVGFrameUserSpaceMetrics::GetCSSViewportSize() const {
336 return GetCSSViewportSizeFromContext(mFrame
->PresContext());
339 float NonSVGFrameUserSpaceMetrics::GetLineHeight(Type aType
) const {
340 auto* context
= mFrame
->PresContext();
343 const auto lineHeightAu
= ReflowInput::CalcLineHeight(
344 *mFrame
->Style(), context
, mFrame
->GetContent(), NS_UNCONSTRAINEDSIZE
,
346 return CSSPixel::FromAppUnits(lineHeightAu
);
349 return SVGContentUtils::GetLineHeight(
350 context
->Document()->GetRootElement());
352 MOZ_ASSERT_UNREACHABLE("Was a new value added to the enumeration?");
356 float UserSpaceMetricsWithSize::GetAxisLength(uint8_t aCtxType
) const {
357 gfx::Size size
= GetSize();
360 case SVGContentUtils::X
:
363 case SVGContentUtils::Y
:
364 length
= size
.height
;
366 case SVGContentUtils::XY
:
368 SVGContentUtils::ComputeNormalizedHypotenuse(size
.width
, size
.height
);
371 MOZ_ASSERT_UNREACHABLE("Unknown axis type");
375 return FixAxisLength(length
);
378 float SVGAnimatedLength::GetPixelsPerUnit(const SVGElement
* aSVGElement
,
379 uint8_t aUnitType
) const {
380 return SVGLength::GetPixelsPerUnit(SVGElementMetrics(aSVGElement
), aUnitType
,
384 float SVGAnimatedLength::GetPixelsPerUnitWithZoom(const SVGElement
* aSVGElement
,
385 uint8_t aUnitType
) const {
386 return SVGLength::GetPixelsPerUnit(SVGElementMetrics(aSVGElement
), aUnitType
,
390 float SVGAnimatedLength::GetPixelsPerUnitWithZoom(
391 const SVGViewportElement
* aCtx
, uint8_t aUnitType
) const {
392 return SVGLength::GetPixelsPerUnit(SVGElementMetrics(aCtx
, aCtx
), aUnitType
,
396 float SVGAnimatedLength::GetPixelsPerUnitWithZoom(nsIFrame
* aFrame
,
397 uint8_t aUnitType
) const {
398 const nsIContent
* content
= aFrame
->GetContent();
399 MOZ_ASSERT(!content
->IsText(), "Not expecting text content");
400 if (content
->IsSVGElement()) {
401 return SVGLength::GetPixelsPerUnit(
402 SVGElementMetrics(static_cast<const SVGElement
*>(content
)), aUnitType
,
405 return SVGLength::GetPixelsPerUnit(NonSVGFrameUserSpaceMetrics(aFrame
),
406 aUnitType
, mCtxType
, true);
409 float SVGAnimatedLength::GetPixelsPerUnitWithZoom(
410 const UserSpaceMetrics
& aMetrics
, uint8_t aUnitType
) const {
411 return SVGLength::GetPixelsPerUnit(aMetrics
, aUnitType
, mCtxType
, true);
414 void SVGAnimatedLength::SetBaseValueInSpecifiedUnits(float aValue
,
415 SVGElement
* aSVGElement
,
417 if (mIsBaseSet
&& mBaseVal
== aValue
) {
421 AutoChangeLengthNotifier
notifier(this, aSVGElement
, aDoSetAttr
);
430 nsresult
SVGAnimatedLength::ConvertToSpecifiedUnits(uint16_t unitType
,
431 SVGElement
* aSVGElement
) {
432 if (!SVGLength::IsValidUnitType(unitType
)) {
433 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
436 if (mIsBaseSet
&& mBaseUnitType
== uint8_t(unitType
)) return NS_OK
;
438 float valueInSpecifiedUnits
;
440 if (SVGLength::IsAbsoluteUnit(unitType
) &&
441 SVGLength::IsAbsoluteUnit(mBaseUnitType
)) {
442 valueInSpecifiedUnits
=
443 mBaseVal
* SVGLength::GetAbsUnitsPerAbsUnit(unitType
, mBaseUnitType
);
445 float pixelsPerUnit
= GetPixelsPerUnit(aSVGElement
, unitType
);
446 if (pixelsPerUnit
== 0.0f
) {
447 return NS_ERROR_ILLEGAL_VALUE
;
449 float valueInPixels
=
450 mBaseVal
* GetPixelsPerUnit(aSVGElement
, mBaseUnitType
);
451 valueInSpecifiedUnits
= valueInPixels
/ pixelsPerUnit
;
454 if (!std::isfinite(valueInSpecifiedUnits
)) {
455 return NS_ERROR_ILLEGAL_VALUE
;
458 // Even though we're not changing the visual effect this length will have
459 // on the document, we still need to send out notifications in case we have
460 // mutation listeners, since the actual string value of the attribute will
462 AutoChangeLengthNotifier
notifier(this, aSVGElement
);
464 mBaseUnitType
= uint8_t(unitType
);
466 mAnimUnitType
= mBaseUnitType
;
468 // Setting aDoSetAttr to false here will ensure we don't call
469 // Will/DidChangeAngle a second time (and dispatch duplicate notifications).
470 SetBaseValueInSpecifiedUnits(valueInSpecifiedUnits
, aSVGElement
, false);
475 nsresult
SVGAnimatedLength::NewValueSpecifiedUnits(uint16_t aUnitType
,
476 float aValueInSpecifiedUnits
,
477 SVGElement
* aSVGElement
) {
478 NS_ENSURE_FINITE(aValueInSpecifiedUnits
, NS_ERROR_ILLEGAL_VALUE
);
480 if (!SVGLength::IsValidUnitType(aUnitType
)) {
481 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
484 if (mIsBaseSet
&& mBaseVal
== aValueInSpecifiedUnits
&&
485 mBaseUnitType
== uint8_t(aUnitType
)) {
489 AutoChangeLengthNotifier
notifier(this, aSVGElement
);
491 mBaseVal
= aValueInSpecifiedUnits
;
493 mBaseUnitType
= uint8_t(aUnitType
);
496 mAnimUnitType
= mBaseUnitType
;
501 already_AddRefed
<DOMSVGLength
> SVGAnimatedLength::ToDOMBaseVal(
502 SVGElement
* aSVGElement
) {
503 return DOMSVGLength::GetTearOff(this, aSVGElement
, false);
506 already_AddRefed
<DOMSVGLength
> SVGAnimatedLength::ToDOMAnimVal(
507 SVGElement
* aSVGElement
) {
508 return DOMSVGLength::GetTearOff(this, aSVGElement
, true);
513 nsresult
SVGAnimatedLength::SetBaseValueString(const nsAString
& aValueAsString
,
514 SVGElement
* aSVGElement
,
519 if (!GetValueFromString(aValueAsString
, value
, &unitType
)) {
520 return NS_ERROR_DOM_SYNTAX_ERR
;
523 if (mIsBaseSet
&& mBaseVal
== float(value
) &&
524 mBaseUnitType
== uint8_t(unitType
)) {
528 AutoChangeLengthNotifier
notifier(this, aSVGElement
, aDoSetAttr
);
532 mBaseUnitType
= uint8_t(unitType
);
535 mAnimUnitType
= mBaseUnitType
;
541 void SVGAnimatedLength::GetBaseValueString(nsAString
& aValueAsString
) const {
542 GetValueString(aValueAsString
, mBaseVal
, mBaseUnitType
);
545 void SVGAnimatedLength::GetAnimValueString(nsAString
& aValueAsString
) const {
546 GetValueString(aValueAsString
, mAnimVal
, mAnimUnitType
);
549 nsresult
SVGAnimatedLength::SetBaseValue(float aValue
, SVGElement
* aSVGElement
,
551 float pixelsPerUnit
= GetPixelsPerUnit(aSVGElement
, mBaseUnitType
);
552 if (pixelsPerUnit
== 0.0f
) {
553 return NS_ERROR_ILLEGAL_VALUE
;
556 float valueInSpecifiedUnits
= aValue
/ pixelsPerUnit
;
557 if (!std::isfinite(valueInSpecifiedUnits
)) {
558 return NS_ERROR_ILLEGAL_VALUE
;
561 SetBaseValueInSpecifiedUnits(valueInSpecifiedUnits
, aSVGElement
, aDoSetAttr
);
565 void SVGAnimatedLength::SetAnimValueInSpecifiedUnits(float aValue
,
566 SVGElement
* aSVGElement
) {
567 if (mAnimVal
== aValue
&& mIsAnimated
) {
572 aSVGElement
->DidAnimateLength(mAttrEnum
);
575 void SVGAnimatedLength::SetAnimValue(float aValue
, uint16_t aUnitType
,
576 SVGElement
* aSVGElement
) {
577 if (mIsAnimated
&& mAnimVal
== aValue
&& mAnimUnitType
== aUnitType
) {
581 mAnimUnitType
= aUnitType
;
583 aSVGElement
->DidAnimateLength(mAttrEnum
);
586 already_AddRefed
<DOMSVGAnimatedLength
> SVGAnimatedLength::ToDOMAnimatedLength(
587 SVGElement
* aSVGElement
) {
588 RefPtr
<DOMSVGAnimatedLength
> svgAnimatedLength
=
589 sSVGAnimatedLengthTearoffTable
.GetTearoff(this);
590 if (!svgAnimatedLength
) {
591 svgAnimatedLength
= new DOMSVGAnimatedLength(this, aSVGElement
);
592 sSVGAnimatedLengthTearoffTable
.AddTearoff(this, svgAnimatedLength
);
595 return svgAnimatedLength
.forget();
598 DOMSVGAnimatedLength::~DOMSVGAnimatedLength() {
599 sSVGAnimatedLengthTearoffTable
.RemoveTearoff(mVal
);
602 UniquePtr
<SMILAttr
> SVGAnimatedLength::ToSMILAttr(SVGElement
* aSVGElement
) {
603 return MakeUnique
<SMILLength
>(this, aSVGElement
);
606 nsresult
SVGAnimatedLength::SMILLength::ValueFromString(
607 const nsAString
& aStr
, const SVGAnimationElement
* /*aSrcElement*/,
608 SMILValue
& aValue
, bool& aPreventCachingOfSandwich
) const {
612 if (!GetValueFromString(aStr
, value
, &unitType
)) {
613 return NS_ERROR_DOM_SYNTAX_ERR
;
616 SMILValue
val(SVGLengthSMILType::Singleton());
617 SVGLengthAndInfo
* lai
= static_cast<SVGLengthAndInfo
*>(val
.mU
.mPtr
);
618 lai
->Set(value
, unitType
, mVal
->GetCtxType());
619 lai
->SetInfo(mSVGElement
);
620 aValue
= std::move(val
);
621 aPreventCachingOfSandwich
= !SVGLength::IsAbsoluteUnit(unitType
);
626 SMILValue
SVGAnimatedLength::SMILLength::GetBaseValue() const {
627 SMILValue
val(SVGLengthSMILType::Singleton());
628 auto* lai
= static_cast<SVGLengthAndInfo
*>(val
.mU
.mPtr
);
629 lai
->CopyBaseFrom(*mVal
);
630 lai
->SetInfo(mSVGElement
);
634 void SVGAnimatedLength::SMILLength::ClearAnimValue() {
635 if (mVal
->mIsAnimated
) {
636 mVal
->mIsAnimated
= false;
637 mVal
->mAnimVal
= mVal
->mBaseVal
;
638 mVal
->mAnimUnitType
= mVal
->mBaseUnitType
;
639 mSVGElement
->DidAnimateLength(mVal
->mAttrEnum
);
643 nsresult
SVGAnimatedLength::SMILLength::SetAnimValue(const SMILValue
& aValue
) {
644 NS_ASSERTION(aValue
.mType
== SVGLengthSMILType::Singleton(),
645 "Unexpected type to assign animated value");
646 if (aValue
.mType
== SVGLengthSMILType::Singleton()) {
647 SVGLengthAndInfo
* lai
= static_cast<SVGLengthAndInfo
*>(aValue
.mU
.mPtr
);
648 mVal
->SetAnimValue(lai
->Value(), lai
->UnitType(), mSVGElement
);
653 float SVGLengthAndInfo::ConvertUnits(const SVGLengthAndInfo
& aTo
) const {
654 if (aTo
.mUnitType
== mUnitType
) {
657 return SVGLength(mValue
, mUnitType
)
658 .GetValueInSpecifiedUnit(aTo
.mUnitType
, aTo
.Element(), aTo
.mCtxType
);
661 float SVGLengthAndInfo::ValueInPixels(const UserSpaceMetrics
& aMetrics
) const {
662 return mValue
== 0.0f
? 0.0f
663 : mValue
* SVGLength::GetPixelsPerUnit(
664 aMetrics
, mUnitType
, mCtxType
, false);
667 void SVGLengthAndInfo::Add(const SVGLengthAndInfo
& aValueToAdd
,
669 mElement
= aValueToAdd
.mElement
;
671 SVGElementMetrics
metrics(Element());
673 // We may be dealing with two different length units, so we normalize to
674 // pixels for the add:
676 float currentLength
= ValueInPixels(metrics
);
677 float lengthToAdd
= aValueToAdd
.ValueInPixels(metrics
) * aCount
;
679 // And then we give the resulting value the same units as the value
680 // that we're animating to/by (i.e. the same as aValueToAdd):
681 mUnitType
= aValueToAdd
.mUnitType
;
682 mCtxType
= aValueToAdd
.mCtxType
;
683 mValue
= (currentLength
+ lengthToAdd
) /
684 SVGLength::GetPixelsPerUnit(metrics
, mUnitType
, mCtxType
, false);
687 void SVGLengthAndInfo::Interpolate(const SVGLengthAndInfo
& aStart
,
688 const SVGLengthAndInfo
& aEnd
,
689 double aUnitDistance
,
690 SVGLengthAndInfo
& aResult
) {
691 MOZ_ASSERT(!aStart
.mElement
|| aStart
.mElement
== aEnd
.mElement
,
692 "Should not be interpolating between different elements");
694 float startValue
, endValue
;
695 if (!aStart
.mElement
|| aUnitDistance
> 0.5) {
696 aResult
.mUnitType
= aEnd
.mUnitType
;
697 aResult
.mCtxType
= aEnd
.mCtxType
;
698 startValue
= aStart
.ConvertUnits(aEnd
);
699 endValue
= aEnd
.mValue
;
701 aResult
.mUnitType
= aStart
.mUnitType
;
702 aResult
.mCtxType
= aStart
.mCtxType
;
703 startValue
= aStart
.mValue
;
704 endValue
= aEnd
.ConvertUnits(aStart
);
706 aResult
.mElement
= aEnd
.mElement
;
707 aResult
.mValue
= startValue
+ (endValue
- startValue
) * aUnitDistance
;
710 } // namespace mozilla