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 "SVGLengthListSMILType.h"
8 #include "nsSMILValue.h"
9 #include "SVGLengthList.h"
10 #include "nsMathUtils.h"
11 #include "mozilla/FloatingPoint.h"
17 /*static*/ SVGLengthListSMILType
SVGLengthListSMILType::sSingleton
;
19 //----------------------------------------------------------------------
20 // nsISMILType implementation
23 SVGLengthListSMILType::Init(nsSMILValue
&aValue
) const
25 MOZ_ASSERT(aValue
.IsNull(), "Unexpected value type");
27 SVGLengthListAndInfo
* lengthList
= new SVGLengthListAndInfo();
29 // See the comment documenting Init() in our header file:
30 lengthList
->SetCanZeroPadList(true);
32 aValue
.mU
.mPtr
= lengthList
;
37 SVGLengthListSMILType::Destroy(nsSMILValue
& aValue
) const
39 NS_PRECONDITION(aValue
.mType
== this, "Unexpected SMIL value type");
40 delete static_cast<SVGLengthListAndInfo
*>(aValue
.mU
.mPtr
);
41 aValue
.mU
.mPtr
= nullptr;
42 aValue
.mType
= nsSMILNullType::Singleton();
46 SVGLengthListSMILType::Assign(nsSMILValue
& aDest
,
47 const nsSMILValue
& aSrc
) const
49 NS_PRECONDITION(aDest
.mType
== aSrc
.mType
, "Incompatible SMIL types");
50 NS_PRECONDITION(aDest
.mType
== this, "Unexpected SMIL value");
52 const SVGLengthListAndInfo
* src
=
53 static_cast<const SVGLengthListAndInfo
*>(aSrc
.mU
.mPtr
);
54 SVGLengthListAndInfo
* dest
=
55 static_cast<SVGLengthListAndInfo
*>(aDest
.mU
.mPtr
);
57 return dest
->CopyFrom(*src
);
61 SVGLengthListSMILType::IsEqual(const nsSMILValue
& aLeft
,
62 const nsSMILValue
& aRight
) const
64 NS_PRECONDITION(aLeft
.mType
== aRight
.mType
, "Incompatible SMIL types");
65 NS_PRECONDITION(aLeft
.mType
== this, "Unexpected type for SMIL value");
67 return *static_cast<const SVGLengthListAndInfo
*>(aLeft
.mU
.mPtr
) ==
68 *static_cast<const SVGLengthListAndInfo
*>(aRight
.mU
.mPtr
);
72 SVGLengthListSMILType::Add(nsSMILValue
& aDest
,
73 const nsSMILValue
& aValueToAdd
,
74 uint32_t aCount
) const
76 NS_PRECONDITION(aDest
.mType
== this, "Unexpected SMIL type");
77 NS_PRECONDITION(aValueToAdd
.mType
== this, "Incompatible SMIL type");
79 SVGLengthListAndInfo
& dest
=
80 *static_cast<SVGLengthListAndInfo
*>(aDest
.mU
.mPtr
);
81 const SVGLengthListAndInfo
& valueToAdd
=
82 *static_cast<const SVGLengthListAndInfo
*>(aValueToAdd
.mU
.mPtr
);
84 // To understand this code, see the comments documenting our Init() method,
85 // and documenting SVGLengthListAndInfo::CanZeroPadList().
87 // Note that *this* method actually may safely zero pad a shorter list
88 // regardless of the value returned by CanZeroPadList() for that list,
89 // just so long as the shorter list is being added *to* the longer list
90 // and *not* vice versa! It's okay in the case of adding a shorter list to a
91 // longer list because during the add operation we'll end up adding the
92 // zeros to actual specified values. It's *not* okay in the case of adding a
93 // longer list to a shorter list because then we end up adding to implicit
94 // zeros when we'd actually need to add to whatever the underlying values
95 // should be, not zeros, and those values are not explicit or otherwise
98 if (valueToAdd
.IsIdentity()) { // Adding identity value - no-op
102 if (dest
.IsIdentity()) { // Adding *to* an identity value
103 if (!dest
.SetLength(valueToAdd
.Length())) {
104 return NS_ERROR_OUT_OF_MEMORY
;
106 for (uint32_t i
= 0; i
< dest
.Length(); ++i
) {
107 dest
[i
].SetValueAndUnit(valueToAdd
[i
].GetValueInCurrentUnits() * aCount
,
108 valueToAdd
[i
].GetUnit());
110 dest
.SetInfo(valueToAdd
.Element(), valueToAdd
.Axis(),
111 valueToAdd
.CanZeroPadList()); // propagate target element info!
114 MOZ_ASSERT(dest
.Element() == valueToAdd
.Element(),
115 "adding values from different elements...?");
117 // Zero-pad our |dest| list, if necessary.
118 if (dest
.Length() < valueToAdd
.Length()) {
119 if (!dest
.CanZeroPadList()) {
120 // SVGContentUtils::ReportToConsole
121 return NS_ERROR_FAILURE
;
124 MOZ_ASSERT(valueToAdd
.CanZeroPadList(),
125 "values disagree about attribute's zero-paddibility");
127 uint32_t i
= dest
.Length();
128 if (!dest
.SetLength(valueToAdd
.Length())) {
129 return NS_ERROR_OUT_OF_MEMORY
;
131 for (; i
< valueToAdd
.Length(); ++i
) {
132 dest
[i
].SetValueAndUnit(0.0f
, valueToAdd
[i
].GetUnit());
136 for (uint32_t i
= 0; i
< valueToAdd
.Length(); ++i
) {
138 if (dest
[i
].GetUnit() == valueToAdd
[i
].GetUnit()) {
139 valToAdd
= valueToAdd
[i
].GetValueInCurrentUnits();
141 // If units differ, we use the unit of the item in 'dest'.
142 // We leave it to the frame code to check that values are finite.
143 valToAdd
= valueToAdd
[i
].GetValueInSpecifiedUnit(dest
[i
].GetUnit(),
147 dest
[i
].SetValueAndUnit(
148 dest
[i
].GetValueInCurrentUnits() + valToAdd
* aCount
,
152 // propagate target element info!
153 dest
.SetInfo(valueToAdd
.Element(), valueToAdd
.Axis(),
154 dest
.CanZeroPadList() && valueToAdd
.CanZeroPadList());
160 SVGLengthListSMILType::ComputeDistance(const nsSMILValue
& aFrom
,
161 const nsSMILValue
& aTo
,
162 double& aDistance
) const
164 NS_PRECONDITION(aFrom
.mType
== this, "Unexpected SMIL type");
165 NS_PRECONDITION(aTo
.mType
== this, "Incompatible SMIL type");
167 const SVGLengthListAndInfo
& from
=
168 *static_cast<const SVGLengthListAndInfo
*>(aFrom
.mU
.mPtr
);
169 const SVGLengthListAndInfo
& to
=
170 *static_cast<const SVGLengthListAndInfo
*>(aTo
.mU
.mPtr
);
172 // To understand this code, see the comments documenting our Init() method,
173 // and documenting SVGLengthListAndInfo::CanZeroPadList().
175 NS_ASSERTION((from
.CanZeroPadList() == to
.CanZeroPadList()) ||
176 (from
.CanZeroPadList() && from
.IsEmpty()) ||
177 (to
.CanZeroPadList() && to
.IsEmpty()),
178 "Only \"zero\" nsSMILValues from the SMIL engine should "
179 "return true for CanZeroPadList() when the attribute "
180 "being animated can't be zero padded");
182 if ((from
.Length() < to
.Length() && !from
.CanZeroPadList()) ||
183 (to
.Length() < from
.Length() && !to
.CanZeroPadList())) {
184 // SVGContentUtils::ReportToConsole
185 return NS_ERROR_FAILURE
;
188 // We return the root of the sum of the squares of the deltas between the
189 // user unit values of the lengths at each correspanding index. In the
190 // general case, paced animation is probably not useful, but this strategy at
191 // least does the right thing for paced animation in the face of simple
192 // 'values' lists such as:
194 // values="100 200 300; 101 201 301; 110 210 310"
196 // I.e. half way through the simple duration we'll get "105 205 305".
201 for (; i
< from
.Length() && i
< to
.Length(); ++i
) {
202 double f
= from
[i
].GetValueInUserUnits(from
.Element(), from
.Axis());
203 double t
= to
[i
].GetValueInUserUnits(to
.Element(), to
.Axis());
204 double delta
= t
- f
;
205 total
+= delta
* delta
;
208 // In the case that from.Length() != to.Length(), one of the following loops
209 // will run. (OK since CanZeroPadList()==true for the other list.)
211 for (; i
< from
.Length(); ++i
) {
212 double f
= from
[i
].GetValueInUserUnits(from
.Element(), from
.Axis());
215 for (; i
< to
.Length(); ++i
) {
216 double t
= to
[i
].GetValueInUserUnits(to
.Element(), to
.Axis());
220 float distance
= sqrt(total
);
221 if (!IsFinite(distance
)) {
222 return NS_ERROR_FAILURE
;
224 aDistance
= distance
;
229 SVGLengthListSMILType::Interpolate(const nsSMILValue
& aStartVal
,
230 const nsSMILValue
& aEndVal
,
231 double aUnitDistance
,
232 nsSMILValue
& aResult
) const
234 NS_PRECONDITION(aStartVal
.mType
== aEndVal
.mType
,
235 "Trying to interpolate different types");
236 NS_PRECONDITION(aStartVal
.mType
== this,
237 "Unexpected types for interpolation");
238 NS_PRECONDITION(aResult
.mType
== this, "Unexpected result type");
240 const SVGLengthListAndInfo
& start
=
241 *static_cast<const SVGLengthListAndInfo
*>(aStartVal
.mU
.mPtr
);
242 const SVGLengthListAndInfo
& end
=
243 *static_cast<const SVGLengthListAndInfo
*>(aEndVal
.mU
.mPtr
);
244 SVGLengthListAndInfo
& result
=
245 *static_cast<SVGLengthListAndInfo
*>(aResult
.mU
.mPtr
);
247 // To understand this code, see the comments documenting our Init() method,
248 // and documenting SVGLengthListAndInfo::CanZeroPadList().
250 NS_ASSERTION((start
.CanZeroPadList() == end
.CanZeroPadList()) ||
251 (start
.CanZeroPadList() && start
.IsEmpty()) ||
252 (end
.CanZeroPadList() && end
.IsEmpty()),
253 "Only \"zero\" nsSMILValues from the SMIL engine should "
254 "return true for CanZeroPadList() when the attribute "
255 "being animated can't be zero padded");
257 if ((start
.Length() < end
.Length() && !start
.CanZeroPadList()) ||
258 (end
.Length() < start
.Length() && !end
.CanZeroPadList())) {
259 // SVGContentUtils::ReportToConsole
260 return NS_ERROR_FAILURE
;
263 if (!result
.SetLength(std::max(start
.Length(), end
.Length()))) {
264 return NS_ERROR_OUT_OF_MEMORY
;
268 for (; i
< start
.Length() && i
< end
.Length(); ++i
) {
270 if (start
[i
].GetUnit() == end
[i
].GetUnit()) {
271 s
= start
[i
].GetValueInCurrentUnits();
273 // If units differ, we use the unit of the item in 'end'.
274 // We leave it to the frame code to check that values are finite.
275 s
= start
[i
].GetValueInSpecifiedUnit(end
[i
].GetUnit(), end
.Element(), end
.Axis());
277 float e
= end
[i
].GetValueInCurrentUnits();
278 result
[i
].SetValueAndUnit(s
+ (e
- s
) * aUnitDistance
, end
[i
].GetUnit());
281 // In the case that start.Length() != end.Length(), one of the following
282 // loops will run. (Okay, since CanZeroPadList()==true for the other list.)
284 for (; i
< start
.Length(); ++i
) {
285 result
[i
].SetValueAndUnit(start
[i
].GetValueInCurrentUnits() -
286 start
[i
].GetValueInCurrentUnits() * aUnitDistance
,
289 for (; i
< end
.Length(); ++i
) {
290 result
[i
].SetValueAndUnit(end
[i
].GetValueInCurrentUnits() * aUnitDistance
,
294 // propagate target element info!
295 result
.SetInfo(end
.Element(), end
.Axis(),
296 start
.CanZeroPadList() && end
.CanZeroPadList());
301 } // namespace mozilla