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"
9 #include "mozilla/FloatingPoint.h"
10 #include "mozilla/SMILValue.h"
11 #include "nsMathUtils.h"
12 #include "SVGLengthList.h"
19 SVGLengthListSMILType
SVGLengthListSMILType::sSingleton
;
21 //----------------------------------------------------------------------
22 // nsISMILType implementation
24 void SVGLengthListSMILType::Init(SMILValue
& 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
;
36 void SVGLengthListSMILType::Destroy(SMILValue
& aValue
) const {
37 MOZ_ASSERT(aValue
.mType
== this, "Unexpected SMIL value type");
38 delete static_cast<SVGLengthListAndInfo
*>(aValue
.mU
.mPtr
);
39 aValue
.mU
.mPtr
= nullptr;
40 aValue
.mType
= SMILNullType::Singleton();
43 nsresult
SVGLengthListSMILType::Assign(SMILValue
& aDest
,
44 const SMILValue
& aSrc
) const {
45 MOZ_ASSERT(aDest
.mType
== aSrc
.mType
, "Incompatible SMIL types");
46 MOZ_ASSERT(aDest
.mType
== this, "Unexpected SMIL value");
48 const SVGLengthListAndInfo
* src
=
49 static_cast<const SVGLengthListAndInfo
*>(aSrc
.mU
.mPtr
);
50 SVGLengthListAndInfo
* dest
=
51 static_cast<SVGLengthListAndInfo
*>(aDest
.mU
.mPtr
);
53 return dest
->CopyFrom(*src
);
56 bool SVGLengthListSMILType::IsEqual(const SMILValue
& aLeft
,
57 const SMILValue
& aRight
) const {
58 MOZ_ASSERT(aLeft
.mType
== aRight
.mType
, "Incompatible SMIL types");
59 MOZ_ASSERT(aLeft
.mType
== this, "Unexpected type for SMIL value");
61 return *static_cast<const SVGLengthListAndInfo
*>(aLeft
.mU
.mPtr
) ==
62 *static_cast<const SVGLengthListAndInfo
*>(aRight
.mU
.mPtr
);
65 nsresult
SVGLengthListSMILType::Add(SMILValue
& aDest
,
66 const SMILValue
& aValueToAdd
,
67 uint32_t aCount
) const {
68 MOZ_ASSERT(aDest
.mType
== this, "Unexpected SMIL type");
69 MOZ_ASSERT(aValueToAdd
.mType
== this, "Incompatible SMIL type");
71 SVGLengthListAndInfo
& dest
=
72 *static_cast<SVGLengthListAndInfo
*>(aDest
.mU
.mPtr
);
73 const SVGLengthListAndInfo
& valueToAdd
=
74 *static_cast<const SVGLengthListAndInfo
*>(aValueToAdd
.mU
.mPtr
);
76 // To understand this code, see the comments documenting our Init() method,
77 // and documenting SVGLengthListAndInfo::CanZeroPadList().
79 // Note that *this* method actually may safely zero pad a shorter list
80 // regardless of the value returned by CanZeroPadList() for that list,
81 // just so long as the shorter list is being added *to* the longer list
82 // and *not* vice versa! It's okay in the case of adding a shorter list to a
83 // longer list because during the add operation we'll end up adding the
84 // zeros to actual specified values. It's *not* okay in the case of adding a
85 // longer list to a shorter list because then we end up adding to implicit
86 // zeros when we'd actually need to add to whatever the underlying values
87 // should be, not zeros, and those values are not explicit or otherwise
90 if (valueToAdd
.IsIdentity()) { // Adding identity value - no-op
94 if (dest
.IsIdentity()) { // Adding *to* an identity value
95 if (!dest
.SetLength(valueToAdd
.Length())) {
96 return NS_ERROR_OUT_OF_MEMORY
;
98 for (uint32_t i
= 0; i
< dest
.Length(); ++i
) {
99 dest
[i
].SetValueAndUnit(valueToAdd
[i
].GetValueInCurrentUnits() * aCount
,
100 valueToAdd
[i
].GetUnit());
103 valueToAdd
.Element(), valueToAdd
.Axis(),
104 valueToAdd
.CanZeroPadList()); // propagate target element info!
107 MOZ_ASSERT(dest
.Element() == valueToAdd
.Element(),
108 "adding values from different elements...?");
110 // Zero-pad our |dest| list, if necessary.
111 if (dest
.Length() < valueToAdd
.Length()) {
112 if (!dest
.CanZeroPadList()) {
113 // SVGContentUtils::ReportToConsole
114 return NS_ERROR_FAILURE
;
117 MOZ_ASSERT(valueToAdd
.CanZeroPadList(),
118 "values disagree about attribute's zero-paddibility");
120 uint32_t i
= dest
.Length();
121 if (!dest
.SetLength(valueToAdd
.Length())) {
122 return NS_ERROR_OUT_OF_MEMORY
;
124 for (; i
< valueToAdd
.Length(); ++i
) {
125 dest
[i
].SetValueAndUnit(0.0f
, valueToAdd
[i
].GetUnit());
129 for (uint32_t i
= 0; i
< valueToAdd
.Length(); ++i
) {
131 if (dest
[i
].GetUnit() == valueToAdd
[i
].GetUnit()) {
132 valToAdd
= valueToAdd
[i
].GetValueInCurrentUnits();
134 // If units differ, we use the unit of the item in 'dest'.
135 // We leave it to the frame code to check that values are finite.
136 valToAdd
= valueToAdd
[i
].GetValueInSpecifiedUnit(
137 dest
[i
].GetUnit(), dest
.Element(), dest
.Axis());
139 dest
[i
].SetValueAndUnit(
140 dest
[i
].GetValueInCurrentUnits() + valToAdd
* aCount
,
144 // propagate target element info!
145 dest
.SetInfo(valueToAdd
.Element(), valueToAdd
.Axis(),
146 dest
.CanZeroPadList() && valueToAdd
.CanZeroPadList());
151 nsresult
SVGLengthListSMILType::ComputeDistance(const SMILValue
& aFrom
,
152 const SMILValue
& aTo
,
153 double& aDistance
) const {
154 MOZ_ASSERT(aFrom
.mType
== this, "Unexpected SMIL type");
155 MOZ_ASSERT(aTo
.mType
== this, "Incompatible SMIL type");
157 const SVGLengthListAndInfo
& from
=
158 *static_cast<const SVGLengthListAndInfo
*>(aFrom
.mU
.mPtr
);
159 const SVGLengthListAndInfo
& to
=
160 *static_cast<const SVGLengthListAndInfo
*>(aTo
.mU
.mPtr
);
162 // To understand this code, see the comments documenting our Init() method,
163 // and documenting SVGLengthListAndInfo::CanZeroPadList().
165 NS_ASSERTION((from
.CanZeroPadList() == to
.CanZeroPadList()) ||
166 (from
.CanZeroPadList() && from
.IsEmpty()) ||
167 (to
.CanZeroPadList() && to
.IsEmpty()),
168 "Only \"zero\" SMILValues from the SMIL engine should "
169 "return true for CanZeroPadList() when the attribute "
170 "being animated can't be zero padded");
172 if ((from
.Length() < to
.Length() && !from
.CanZeroPadList()) ||
173 (to
.Length() < from
.Length() && !to
.CanZeroPadList())) {
174 // SVGContentUtils::ReportToConsole
175 return NS_ERROR_FAILURE
;
178 // We return the root of the sum of the squares of the deltas between the
179 // user unit values of the lengths at each correspanding index. In the
180 // general case, paced animation is probably not useful, but this strategy at
181 // least does the right thing for paced animation in the face of simple
182 // 'values' lists such as:
184 // values="100 200 300; 101 201 301; 110 210 310"
186 // I.e. half way through the simple duration we'll get "105 205 305".
191 for (; i
< from
.Length() && i
< to
.Length(); ++i
) {
192 double f
= from
[i
].GetValueInUserUnits(from
.Element(), from
.Axis());
193 double t
= to
[i
].GetValueInUserUnits(to
.Element(), to
.Axis());
194 double delta
= t
- f
;
195 total
+= delta
* delta
;
198 // In the case that from.Length() != to.Length(), one of the following loops
199 // will run. (OK since CanZeroPadList()==true for the other list.)
201 for (; i
< from
.Length(); ++i
) {
202 double f
= from
[i
].GetValueInUserUnits(from
.Element(), from
.Axis());
205 for (; i
< to
.Length(); ++i
) {
206 double t
= to
[i
].GetValueInUserUnits(to
.Element(), to
.Axis());
210 float distance
= sqrt(total
);
211 if (!IsFinite(distance
)) {
212 return NS_ERROR_FAILURE
;
214 aDistance
= distance
;
218 nsresult
SVGLengthListSMILType::Interpolate(const SMILValue
& aStartVal
,
219 const SMILValue
& aEndVal
,
220 double aUnitDistance
,
221 SMILValue
& aResult
) const {
222 MOZ_ASSERT(aStartVal
.mType
== aEndVal
.mType
,
223 "Trying to interpolate different types");
224 MOZ_ASSERT(aStartVal
.mType
== this, "Unexpected types for interpolation");
225 MOZ_ASSERT(aResult
.mType
== this, "Unexpected result type");
227 const SVGLengthListAndInfo
& start
=
228 *static_cast<const SVGLengthListAndInfo
*>(aStartVal
.mU
.mPtr
);
229 const SVGLengthListAndInfo
& end
=
230 *static_cast<const SVGLengthListAndInfo
*>(aEndVal
.mU
.mPtr
);
231 SVGLengthListAndInfo
& result
=
232 *static_cast<SVGLengthListAndInfo
*>(aResult
.mU
.mPtr
);
234 // To understand this code, see the comments documenting our Init() method,
235 // and documenting SVGLengthListAndInfo::CanZeroPadList().
237 NS_ASSERTION((start
.CanZeroPadList() == end
.CanZeroPadList()) ||
238 (start
.CanZeroPadList() && start
.IsEmpty()) ||
239 (end
.CanZeroPadList() && end
.IsEmpty()),
240 "Only \"zero\" SMILValues from the SMIL engine should "
241 "return true for CanZeroPadList() when the attribute "
242 "being animated can't be zero padded");
244 if ((start
.Length() < end
.Length() && !start
.CanZeroPadList()) ||
245 (end
.Length() < start
.Length() && !end
.CanZeroPadList())) {
246 // SVGContentUtils::ReportToConsole
247 return NS_ERROR_FAILURE
;
250 if (!result
.SetLength(std::max(start
.Length(), end
.Length()))) {
251 return NS_ERROR_OUT_OF_MEMORY
;
255 for (; i
< start
.Length() && i
< end
.Length(); ++i
) {
257 if (start
[i
].GetUnit() == end
[i
].GetUnit()) {
258 s
= start
[i
].GetValueInCurrentUnits();
260 // If units differ, we use the unit of the item in 'end'.
261 // We leave it to the frame code to check that values are finite.
262 s
= start
[i
].GetValueInSpecifiedUnit(end
[i
].GetUnit(), end
.Element(),
265 float e
= end
[i
].GetValueInCurrentUnits();
266 result
[i
].SetValueAndUnit(s
+ (e
- s
) * aUnitDistance
, end
[i
].GetUnit());
269 // In the case that start.Length() != end.Length(), one of the following
270 // loops will run. (Okay, since CanZeroPadList()==true for the other list.)
272 for (; i
< start
.Length(); ++i
) {
273 result
[i
].SetValueAndUnit(
274 start
[i
].GetValueInCurrentUnits() -
275 start
[i
].GetValueInCurrentUnits() * aUnitDistance
,
278 for (; i
< end
.Length(); ++i
) {
279 result
[i
].SetValueAndUnit(end
[i
].GetValueInCurrentUnits() * aUnitDistance
,
283 // propagate target element info!
284 result
.SetInfo(end
.Element(), end
.Axis(),
285 start
.CanZeroPadList() && end
.CanZeroPadList());
290 } // namespace mozilla