Line data Source code
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/. */
6 :
7 : #include "SVGLengthListSMILType.h"
8 : #include "nsSMILValue.h"
9 : #include "SVGLengthList.h"
10 : #include "nsMathUtils.h"
11 : #include "mozilla/FloatingPoint.h"
12 : #include <math.h>
13 : #include <algorithm>
14 :
15 : namespace mozilla {
16 :
17 : /*static*/ SVGLengthListSMILType SVGLengthListSMILType::sSingleton;
18 :
19 : //----------------------------------------------------------------------
20 : // nsISMILType implementation
21 :
22 : void
23 0 : SVGLengthListSMILType::Init(nsSMILValue &aValue) const
24 : {
25 0 : MOZ_ASSERT(aValue.IsNull(), "Unexpected value type");
26 :
27 0 : SVGLengthListAndInfo* lengthList = new SVGLengthListAndInfo();
28 :
29 : // See the comment documenting Init() in our header file:
30 0 : lengthList->SetCanZeroPadList(true);
31 :
32 0 : aValue.mU.mPtr = lengthList;
33 0 : aValue.mType = this;
34 0 : }
35 :
36 : void
37 0 : SVGLengthListSMILType::Destroy(nsSMILValue& aValue) const
38 : {
39 0 : NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type");
40 0 : delete static_cast<SVGLengthListAndInfo*>(aValue.mU.mPtr);
41 0 : aValue.mU.mPtr = nullptr;
42 0 : aValue.mType = nsSMILNullType::Singleton();
43 0 : }
44 :
45 : nsresult
46 0 : SVGLengthListSMILType::Assign(nsSMILValue& aDest,
47 : const nsSMILValue& aSrc) const
48 : {
49 0 : NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types");
50 0 : NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value");
51 :
52 : const SVGLengthListAndInfo* src =
53 0 : static_cast<const SVGLengthListAndInfo*>(aSrc.mU.mPtr);
54 : SVGLengthListAndInfo* dest =
55 0 : static_cast<SVGLengthListAndInfo*>(aDest.mU.mPtr);
56 :
57 0 : return dest->CopyFrom(*src);
58 : }
59 :
60 : bool
61 0 : SVGLengthListSMILType::IsEqual(const nsSMILValue& aLeft,
62 : const nsSMILValue& aRight) const
63 : {
64 0 : NS_PRECONDITION(aLeft.mType == aRight.mType, "Incompatible SMIL types");
65 0 : NS_PRECONDITION(aLeft.mType == this, "Unexpected type for SMIL value");
66 :
67 0 : return *static_cast<const SVGLengthListAndInfo*>(aLeft.mU.mPtr) ==
68 0 : *static_cast<const SVGLengthListAndInfo*>(aRight.mU.mPtr);
69 : }
70 :
71 : nsresult
72 0 : SVGLengthListSMILType::Add(nsSMILValue& aDest,
73 : const nsSMILValue& aValueToAdd,
74 : uint32_t aCount) const
75 : {
76 0 : NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type");
77 0 : NS_PRECONDITION(aValueToAdd.mType == this, "Incompatible SMIL type");
78 :
79 : SVGLengthListAndInfo& dest =
80 0 : *static_cast<SVGLengthListAndInfo*>(aDest.mU.mPtr);
81 : const SVGLengthListAndInfo& valueToAdd =
82 0 : *static_cast<const SVGLengthListAndInfo*>(aValueToAdd.mU.mPtr);
83 :
84 : // To understand this code, see the comments documenting our Init() method,
85 : // and documenting SVGLengthListAndInfo::CanZeroPadList().
86 :
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
96 : // available.
97 :
98 0 : if (valueToAdd.IsIdentity()) { // Adding identity value - no-op
99 0 : return NS_OK;
100 : }
101 :
102 0 : if (dest.IsIdentity()) { // Adding *to* an identity value
103 0 : if (!dest.SetLength(valueToAdd.Length())) {
104 0 : return NS_ERROR_OUT_OF_MEMORY;
105 : }
106 0 : for (uint32_t i = 0; i < dest.Length(); ++i) {
107 0 : dest[i].SetValueAndUnit(valueToAdd[i].GetValueInCurrentUnits() * aCount,
108 0 : valueToAdd[i].GetUnit());
109 : }
110 0 : dest.SetInfo(valueToAdd.Element(), valueToAdd.Axis(),
111 0 : valueToAdd.CanZeroPadList()); // propagate target element info!
112 0 : return NS_OK;
113 : }
114 0 : MOZ_ASSERT(dest.Element() == valueToAdd.Element(),
115 : "adding values from different elements...?");
116 :
117 : // Zero-pad our |dest| list, if necessary.
118 0 : if (dest.Length() < valueToAdd.Length()) {
119 0 : if (!dest.CanZeroPadList()) {
120 : // SVGContentUtils::ReportToConsole
121 0 : return NS_ERROR_FAILURE;
122 : }
123 :
124 0 : MOZ_ASSERT(valueToAdd.CanZeroPadList(),
125 : "values disagree about attribute's zero-paddibility");
126 :
127 0 : uint32_t i = dest.Length();
128 0 : if (!dest.SetLength(valueToAdd.Length())) {
129 0 : return NS_ERROR_OUT_OF_MEMORY;
130 : }
131 0 : for (; i < valueToAdd.Length(); ++i) {
132 0 : dest[i].SetValueAndUnit(0.0f, valueToAdd[i].GetUnit());
133 : }
134 : }
135 :
136 0 : for (uint32_t i = 0; i < valueToAdd.Length(); ++i) {
137 : float valToAdd;
138 0 : if (dest[i].GetUnit() == valueToAdd[i].GetUnit()) {
139 0 : valToAdd = valueToAdd[i].GetValueInCurrentUnits();
140 : } else {
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 0 : valToAdd = valueToAdd[i].GetValueInSpecifiedUnit(dest[i].GetUnit(),
144 0 : dest.Element(),
145 0 : dest.Axis());
146 : }
147 0 : dest[i].SetValueAndUnit(
148 0 : dest[i].GetValueInCurrentUnits() + valToAdd * aCount,
149 0 : dest[i].GetUnit());
150 : }
151 :
152 : // propagate target element info!
153 0 : dest.SetInfo(valueToAdd.Element(), valueToAdd.Axis(),
154 0 : dest.CanZeroPadList() && valueToAdd.CanZeroPadList());
155 :
156 0 : return NS_OK;
157 : }
158 :
159 : nsresult
160 0 : SVGLengthListSMILType::ComputeDistance(const nsSMILValue& aFrom,
161 : const nsSMILValue& aTo,
162 : double& aDistance) const
163 : {
164 0 : NS_PRECONDITION(aFrom.mType == this, "Unexpected SMIL type");
165 0 : NS_PRECONDITION(aTo.mType == this, "Incompatible SMIL type");
166 :
167 : const SVGLengthListAndInfo& from =
168 0 : *static_cast<const SVGLengthListAndInfo*>(aFrom.mU.mPtr);
169 : const SVGLengthListAndInfo& to =
170 0 : *static_cast<const SVGLengthListAndInfo*>(aTo.mU.mPtr);
171 :
172 : // To understand this code, see the comments documenting our Init() method,
173 : // and documenting SVGLengthListAndInfo::CanZeroPadList().
174 :
175 0 : 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");
181 :
182 0 : if ((from.Length() < to.Length() && !from.CanZeroPadList()) ||
183 0 : (to.Length() < from.Length() && !to.CanZeroPadList())) {
184 : // SVGContentUtils::ReportToConsole
185 0 : return NS_ERROR_FAILURE;
186 : }
187 :
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:
193 : //
194 : // values="100 200 300; 101 201 301; 110 210 310"
195 : //
196 : // I.e. half way through the simple duration we'll get "105 205 305".
197 :
198 0 : double total = 0.0;
199 :
200 0 : uint32_t i = 0;
201 0 : for (; i < from.Length() && i < to.Length(); ++i) {
202 0 : double f = from[i].GetValueInUserUnits(from.Element(), from.Axis());
203 0 : double t = to[i].GetValueInUserUnits(to.Element(), to.Axis());
204 0 : double delta = t - f;
205 0 : total += delta * delta;
206 : }
207 :
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.)
210 :
211 0 : for (; i < from.Length(); ++i) {
212 0 : double f = from[i].GetValueInUserUnits(from.Element(), from.Axis());
213 0 : total += f * f;
214 : }
215 0 : for (; i < to.Length(); ++i) {
216 0 : double t = to[i].GetValueInUserUnits(to.Element(), to.Axis());
217 0 : total += t * t;
218 : }
219 :
220 0 : float distance = sqrt(total);
221 0 : if (!IsFinite(distance)) {
222 0 : return NS_ERROR_FAILURE;
223 : }
224 0 : aDistance = distance;
225 0 : return NS_OK;
226 : }
227 :
228 : nsresult
229 0 : SVGLengthListSMILType::Interpolate(const nsSMILValue& aStartVal,
230 : const nsSMILValue& aEndVal,
231 : double aUnitDistance,
232 : nsSMILValue& aResult) const
233 : {
234 0 : NS_PRECONDITION(aStartVal.mType == aEndVal.mType,
235 : "Trying to interpolate different types");
236 0 : NS_PRECONDITION(aStartVal.mType == this,
237 : "Unexpected types for interpolation");
238 0 : NS_PRECONDITION(aResult.mType == this, "Unexpected result type");
239 :
240 : const SVGLengthListAndInfo& start =
241 0 : *static_cast<const SVGLengthListAndInfo*>(aStartVal.mU.mPtr);
242 : const SVGLengthListAndInfo& end =
243 0 : *static_cast<const SVGLengthListAndInfo*>(aEndVal.mU.mPtr);
244 : SVGLengthListAndInfo& result =
245 0 : *static_cast<SVGLengthListAndInfo*>(aResult.mU.mPtr);
246 :
247 : // To understand this code, see the comments documenting our Init() method,
248 : // and documenting SVGLengthListAndInfo::CanZeroPadList().
249 :
250 0 : 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");
256 :
257 0 : if ((start.Length() < end.Length() && !start.CanZeroPadList()) ||
258 0 : (end.Length() < start.Length() && !end.CanZeroPadList())) {
259 : // SVGContentUtils::ReportToConsole
260 0 : return NS_ERROR_FAILURE;
261 : }
262 :
263 0 : if (!result.SetLength(std::max(start.Length(), end.Length()))) {
264 0 : return NS_ERROR_OUT_OF_MEMORY;
265 : }
266 :
267 0 : uint32_t i = 0;
268 0 : for (; i < start.Length() && i < end.Length(); ++i) {
269 : float s;
270 0 : if (start[i].GetUnit() == end[i].GetUnit()) {
271 0 : s = start[i].GetValueInCurrentUnits();
272 : } else {
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 0 : s = start[i].GetValueInSpecifiedUnit(end[i].GetUnit(), end.Element(), end.Axis());
276 : }
277 0 : float e = end[i].GetValueInCurrentUnits();
278 0 : result[i].SetValueAndUnit(s + (e - s) * aUnitDistance, end[i].GetUnit());
279 : }
280 :
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.)
283 :
284 0 : for (; i < start.Length(); ++i) {
285 0 : result[i].SetValueAndUnit(start[i].GetValueInCurrentUnits() -
286 0 : start[i].GetValueInCurrentUnits() * aUnitDistance,
287 0 : start[i].GetUnit());
288 : }
289 0 : for (; i < end.Length(); ++i) {
290 0 : result[i].SetValueAndUnit(end[i].GetValueInCurrentUnits() * aUnitDistance,
291 0 : end[i].GetUnit());
292 : }
293 :
294 : // propagate target element info!
295 0 : result.SetInfo(end.Element(), end.Axis(),
296 0 : start.CanZeroPadList() && end.CanZeroPadList());
297 :
298 0 : return NS_OK;
299 : }
300 :
301 : } // namespace mozilla
|