1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Mozilla SVG project.
16 : *
17 : * The Initial Developer of the Original Code is the Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2010
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either of the GNU General Public License Version 2 or later (the "GPL"),
25 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "SVGLengthListSMILType.h"
38 : #include "nsSMILValue.h"
39 : #include "SVGLengthList.h"
40 : #include "nsMathUtils.h"
41 : #include <math.h>
42 :
43 : namespace mozilla {
44 :
45 1464 : /*static*/ SVGLengthListSMILType SVGLengthListSMILType::sSingleton;
46 :
47 : //----------------------------------------------------------------------
48 : // nsISMILType implementation
49 :
50 : void
51 0 : SVGLengthListSMILType::Init(nsSMILValue &aValue) const
52 : {
53 0 : NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected value type");
54 :
55 0 : SVGLengthListAndInfo* lengthList = new SVGLengthListAndInfo();
56 :
57 : // See the comment documenting Init() in our header file:
58 0 : lengthList->SetCanZeroPadList(true);
59 :
60 0 : aValue.mU.mPtr = lengthList;
61 0 : aValue.mType = this;
62 0 : }
63 :
64 : void
65 0 : SVGLengthListSMILType::Destroy(nsSMILValue& aValue) const
66 : {
67 0 : NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type");
68 0 : delete static_cast<SVGLengthListAndInfo*>(aValue.mU.mPtr);
69 0 : aValue.mU.mPtr = nsnull;
70 0 : aValue.mType = &nsSMILNullType::sSingleton;
71 0 : }
72 :
73 : nsresult
74 0 : SVGLengthListSMILType::Assign(nsSMILValue& aDest,
75 : const nsSMILValue& aSrc) const
76 : {
77 0 : NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types");
78 0 : NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value");
79 :
80 : const SVGLengthListAndInfo* src =
81 0 : static_cast<const SVGLengthListAndInfo*>(aSrc.mU.mPtr);
82 : SVGLengthListAndInfo* dest =
83 0 : static_cast<SVGLengthListAndInfo*>(aDest.mU.mPtr);
84 :
85 0 : return dest->CopyFrom(*src);
86 : }
87 :
88 : bool
89 0 : SVGLengthListSMILType::IsEqual(const nsSMILValue& aLeft,
90 : const nsSMILValue& aRight) const
91 : {
92 0 : NS_PRECONDITION(aLeft.mType == aRight.mType, "Incompatible SMIL types");
93 0 : NS_PRECONDITION(aLeft.mType == this, "Unexpected type for SMIL value");
94 :
95 : return *static_cast<const SVGLengthListAndInfo*>(aLeft.mU.mPtr) ==
96 0 : *static_cast<const SVGLengthListAndInfo*>(aRight.mU.mPtr);
97 : }
98 :
99 : nsresult
100 0 : SVGLengthListSMILType::Add(nsSMILValue& aDest,
101 : const nsSMILValue& aValueToAdd,
102 : PRUint32 aCount) const
103 : {
104 0 : NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type");
105 0 : NS_PRECONDITION(aValueToAdd.mType == this, "Incompatible SMIL type");
106 :
107 : SVGLengthListAndInfo& dest =
108 0 : *static_cast<SVGLengthListAndInfo*>(aDest.mU.mPtr);
109 : const SVGLengthListAndInfo& valueToAdd =
110 0 : *static_cast<const SVGLengthListAndInfo*>(aValueToAdd.mU.mPtr);
111 :
112 : // To understand this code, see the comments documenting our Init() method,
113 : // and documenting SVGLengthListAndInfo::CanZeroPadList().
114 :
115 : // Note that *this* method actually may safely zero pad a shorter list
116 : // regardless of the value returned by CanZeroPadList() for that list,
117 : // just so long as the shorter list is being added *to* the longer list
118 : // and *not* vice versa! It's okay in the case of adding a shorter list to a
119 : // longer list because during the add operation we'll end up adding the
120 : // zeros to actual specified values. It's *not* okay in the case of adding a
121 : // longer list to a shorter list because then we end up adding to implicit
122 : // zeros when we'd actually need to add to whatever the underlying values
123 : // should be, not zeros, and those values are not explicit or otherwise
124 : // available.
125 :
126 0 : if (dest.IsEmpty() && valueToAdd.IsEmpty()) {
127 : // Adding two identity values, no-op. This occurs when performing a
128 : // discrete by-animation on an attribute with no specified base value.
129 0 : return NS_OK;
130 : }
131 :
132 0 : if (!valueToAdd.Element()) { // Adding identity value - no-op
133 0 : NS_ABORT_IF_FALSE(valueToAdd.IsEmpty(),
134 : "Identity values should be empty");
135 0 : return NS_OK;
136 : }
137 :
138 0 : if (!dest.Element()) { // Adding *to* an identity value
139 0 : NS_ABORT_IF_FALSE(dest.IsEmpty(),
140 : "Identity values should be empty");
141 0 : if (!dest.SetLength(valueToAdd.Length())) {
142 0 : return NS_ERROR_OUT_OF_MEMORY;
143 : }
144 0 : for (PRUint32 i = 0; i < dest.Length(); ++i) {
145 0 : dest[i].SetValueAndUnit(valueToAdd[i].GetValueInCurrentUnits() * aCount,
146 0 : valueToAdd[i].GetUnit());
147 : }
148 0 : dest.SetInfo(valueToAdd.Element(), valueToAdd.Axis(),
149 0 : valueToAdd.CanZeroPadList()); // propagate target element info!
150 0 : return NS_OK;
151 : }
152 0 : NS_ABORT_IF_FALSE(dest.Element() == valueToAdd.Element(),
153 : "adding values from different elements...?");
154 :
155 : // Zero-pad our |dest| list, if necessary.
156 0 : if (dest.Length() < valueToAdd.Length()) {
157 0 : if (!dest.CanZeroPadList()) {
158 : // nsSVGUtils::ReportToConsole
159 0 : return NS_ERROR_FAILURE;
160 : }
161 :
162 0 : NS_ABORT_IF_FALSE(valueToAdd.CanZeroPadList(),
163 : "values disagree about attribute's zero-paddibility");
164 :
165 0 : PRUint32 i = dest.Length();
166 0 : if (!dest.SetLength(valueToAdd.Length())) {
167 0 : return NS_ERROR_OUT_OF_MEMORY;
168 : }
169 0 : for (; i < valueToAdd.Length(); ++i) {
170 0 : dest[i].SetValueAndUnit(0.0f, valueToAdd[i].GetUnit());
171 : }
172 : }
173 :
174 0 : for (PRUint32 i = 0; i < valueToAdd.Length(); ++i) {
175 : float valToAdd;
176 0 : if (dest[i].GetUnit() == valueToAdd[i].GetUnit()) {
177 0 : valToAdd = valueToAdd[i].GetValueInCurrentUnits();
178 : } else {
179 : // If units differ, we use the unit of the item in 'dest'.
180 : // We leave it to the frame code to check that values are finite.
181 0 : valToAdd = valueToAdd[i].GetValueInSpecifiedUnit(dest[i].GetUnit(),
182 0 : dest.Element(),
183 0 : dest.Axis());
184 : }
185 0 : dest[i].SetValueAndUnit(
186 0 : dest[i].GetValueInCurrentUnits() + valToAdd * aCount,
187 0 : dest[i].GetUnit());
188 : }
189 :
190 : // propagate target element info!
191 0 : dest.SetInfo(valueToAdd.Element(), valueToAdd.Axis(),
192 0 : dest.CanZeroPadList() && valueToAdd.CanZeroPadList());
193 :
194 0 : return NS_OK;
195 : }
196 :
197 : nsresult
198 0 : SVGLengthListSMILType::ComputeDistance(const nsSMILValue& aFrom,
199 : const nsSMILValue& aTo,
200 : double& aDistance) const
201 : {
202 0 : NS_PRECONDITION(aFrom.mType == this, "Unexpected SMIL type");
203 0 : NS_PRECONDITION(aTo.mType == this, "Incompatible SMIL type");
204 :
205 : const SVGLengthListAndInfo& from =
206 0 : *static_cast<const SVGLengthListAndInfo*>(aFrom.mU.mPtr);
207 : const SVGLengthListAndInfo& to =
208 0 : *static_cast<const SVGLengthListAndInfo*>(aTo.mU.mPtr);
209 :
210 : // To understand this code, see the comments documenting our Init() method,
211 : // and documenting SVGLengthListAndInfo::CanZeroPadList().
212 :
213 0 : NS_ASSERTION((from.CanZeroPadList() == to.CanZeroPadList()) ||
214 : (from.CanZeroPadList() && from.IsEmpty()) ||
215 : (to.CanZeroPadList() && to.IsEmpty()),
216 : "Only \"zero\" nsSMILValues from the SMIL engine should "
217 : "return true for CanZeroPadList() when the attribute "
218 : "being animated can't be zero padded");
219 :
220 0 : if ((from.Length() < to.Length() && !from.CanZeroPadList()) ||
221 0 : (to.Length() < from.Length() && !to.CanZeroPadList())) {
222 : // nsSVGUtils::ReportToConsole
223 0 : return NS_ERROR_FAILURE;
224 : }
225 :
226 : // We return the root of the sum of the squares of the deltas between the
227 : // user unit values of the lengths at each correspanding index. In the
228 : // general case, paced animation is probably not useful, but this strategy at
229 : // least does the right thing for paced animation in the face of simple
230 : // 'values' lists such as:
231 : //
232 : // values="100 200 300; 101 201 301; 110 210 310"
233 : //
234 : // I.e. half way through the simple duration we'll get "105 205 305".
235 :
236 0 : double total = 0.0;
237 :
238 0 : PRUint32 i = 0;
239 0 : for (; i < from.Length() && i < to.Length(); ++i) {
240 0 : double f = from[i].GetValueInUserUnits(from.Element(), from.Axis());
241 0 : double t = to[i].GetValueInUserUnits(to.Element(), to.Axis());
242 0 : double delta = t - f;
243 0 : total += delta * delta;
244 : }
245 :
246 : // In the case that from.Length() != to.Length(), one of the following loops
247 : // will run. (OK since CanZeroPadList()==true for the other list.)
248 :
249 0 : for (; i < from.Length(); ++i) {
250 0 : double f = from[i].GetValueInUserUnits(from.Element(), from.Axis());
251 0 : total += f * f;
252 : }
253 0 : for (; i < to.Length(); ++i) {
254 0 : double t = to[i].GetValueInUserUnits(to.Element(), to.Axis());
255 0 : total += t * t;
256 : }
257 :
258 0 : float distance = sqrt(total);
259 0 : if (!NS_finite(distance)) {
260 0 : return NS_ERROR_FAILURE;
261 : }
262 0 : aDistance = distance;
263 0 : return NS_OK;
264 : }
265 :
266 : nsresult
267 0 : SVGLengthListSMILType::Interpolate(const nsSMILValue& aStartVal,
268 : const nsSMILValue& aEndVal,
269 : double aUnitDistance,
270 : nsSMILValue& aResult) const
271 : {
272 0 : NS_PRECONDITION(aStartVal.mType == aEndVal.mType,
273 : "Trying to interpolate different types");
274 0 : NS_PRECONDITION(aStartVal.mType == this,
275 : "Unexpected types for interpolation");
276 0 : NS_PRECONDITION(aResult.mType == this, "Unexpected result type");
277 :
278 : const SVGLengthListAndInfo& start =
279 0 : *static_cast<const SVGLengthListAndInfo*>(aStartVal.mU.mPtr);
280 : const SVGLengthListAndInfo& end =
281 0 : *static_cast<const SVGLengthListAndInfo*>(aEndVal.mU.mPtr);
282 : SVGLengthListAndInfo& result =
283 0 : *static_cast<SVGLengthListAndInfo*>(aResult.mU.mPtr);
284 :
285 : // To understand this code, see the comments documenting our Init() method,
286 : // and documenting SVGLengthListAndInfo::CanZeroPadList().
287 :
288 0 : NS_ASSERTION((start.CanZeroPadList() == end.CanZeroPadList()) ||
289 : (start.CanZeroPadList() && start.IsEmpty()) ||
290 : (end.CanZeroPadList() && end.IsEmpty()),
291 : "Only \"zero\" nsSMILValues from the SMIL engine should "
292 : "return true for CanZeroPadList() when the attribute "
293 : "being animated can't be zero padded");
294 :
295 0 : if ((start.Length() < end.Length() && !start.CanZeroPadList()) ||
296 0 : (end.Length() < start.Length() && !end.CanZeroPadList())) {
297 : // nsSVGUtils::ReportToConsole
298 0 : return NS_ERROR_FAILURE;
299 : }
300 :
301 0 : if (!result.SetLength(NS_MAX(start.Length(), end.Length()))) {
302 0 : return NS_ERROR_OUT_OF_MEMORY;
303 : }
304 :
305 0 : PRUint32 i = 0;
306 0 : for (; i < start.Length() && i < end.Length(); ++i) {
307 : float s;
308 0 : if (start[i].GetUnit() == end[i].GetUnit()) {
309 0 : s = start[i].GetValueInCurrentUnits();
310 : } else {
311 : // If units differ, we use the unit of the item in 'end'.
312 : // We leave it to the frame code to check that values are finite.
313 0 : s = start[i].GetValueInSpecifiedUnit(end[i].GetUnit(), end.Element(), end.Axis());
314 : }
315 0 : float e = end[i].GetValueInCurrentUnits();
316 0 : result[i].SetValueAndUnit(s + (e - s) * aUnitDistance, end[i].GetUnit());
317 : }
318 :
319 : // In the case that start.Length() != end.Length(), one of the following
320 : // loops will run. (Okay, since CanZeroPadList()==true for the other list.)
321 :
322 0 : for (; i < start.Length(); ++i) {
323 0 : result[i].SetValueAndUnit(start[i].GetValueInCurrentUnits() -
324 0 : start[i].GetValueInCurrentUnits() * aUnitDistance,
325 0 : start[i].GetUnit());
326 : }
327 0 : for (; i < end.Length(); ++i) {
328 0 : result[i].SetValueAndUnit(end[i].GetValueInCurrentUnits() * aUnitDistance,
329 0 : end[i].GetUnit());
330 : }
331 :
332 : // propagate target element info!
333 0 : result.SetInfo(end.Element(), end.Axis(),
334 0 : start.CanZeroPadList() && end.CanZeroPadList());
335 :
336 0 : return NS_OK;
337 : }
338 :
339 4392 : } // namespace mozilla
|