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 Corporation.
18 : * Portions created by the Initial Developer are Copyright (C) 2010
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Daniel Holbert <dholbert@mozilla.com>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : /* implementation of nsISMILType for use by <animateMotion> element */
39 :
40 : #include "SVGMotionSMILType.h"
41 : #include "nsSMILValue.h"
42 : #include "nsDebug.h"
43 : #include "nsSVGAngle.h"
44 : #include "nsIDOMSVGAngle.h"
45 : #include "nsSVGPathElement.h"
46 : #include "nsIDOMSVGPathSeg.h"
47 : #include "nsIDOMSVGPathSegList.h"
48 : #include "nsMathUtils.h"
49 : #include <math.h>
50 :
51 : namespace mozilla {
52 :
53 1464 : /*static*/ SVGMotionSMILType SVGMotionSMILType::sSingleton;
54 :
55 :
56 : // Helper enum, for distinguishing between types of MotionSegment structs
57 : enum SegmentType {
58 : eSegmentType_Translation,
59 : eSegmentType_PathPoint
60 : };
61 :
62 : // Helper Structs: containers for params to define our MotionSegment
63 : // (either simple translation or point-on-a-path)
64 : struct TranslationParams { // Simple translation
65 : float mX;
66 : float mY;
67 : };
68 : struct PathPointParams { // Point along a path
69 : gfxFlattenedPath* mPath; // NOTE: Refcounted; need to AddRef/Release.
70 : float mDistToPoint; // Distance from path start to the point on the path that
71 : // we're interested in.
72 : };
73 :
74 : /**
75 : * Helper Struct: MotionSegment
76 : *
77 : * Instances of this class represent the points that we move between during
78 : * <animateMotion>. Each nsSMILValue will get a nsTArray of these (generally
79 : * with at most 1 entry in the array, except for in SandwichAdd). (This
80 : * matches our behavior in nsSVGTransformSMILType.)
81 : *
82 : * NOTE: In general, MotionSegments are represented as points on a path
83 : * (eSegmentType_PathPoint), so that we can easily interpolate and compute
84 : * distance *along their path*. However, Add() outputs MotionSegments as
85 : * simple translations (eSegmentType_Translation), because adding two points
86 : * from a path (e.g. when accumulating a repeated animation) will generally
87 : * take you to an arbitrary point *off* of the path.
88 : */
89 : struct MotionSegment
90 : {
91 : // Default constructor just locks us into being a Translation, and leaves
92 : // other fields uninitialized (since client is presumably about to set them)
93 : MotionSegment()
94 : : mSegmentType(eSegmentType_Translation)
95 : { }
96 :
97 : // Constructor for a translation
98 0 : MotionSegment(float aX, float aY, float aRotateAngle)
99 : : mRotateType(eRotateType_Explicit), mRotateAngle(aRotateAngle),
100 0 : mSegmentType(eSegmentType_Translation)
101 : {
102 0 : mU.mTranslationParams.mX = aX;
103 0 : mU.mTranslationParams.mY = aY;
104 0 : }
105 :
106 : // Constructor for a point on a path (NOTE: AddRef's)
107 0 : MotionSegment(gfxFlattenedPath* aPath, float aDistToPoint,
108 : RotateType aRotateType, float aRotateAngle)
109 : : mRotateType(aRotateType), mRotateAngle(aRotateAngle),
110 0 : mSegmentType(eSegmentType_PathPoint)
111 : {
112 0 : mU.mPathPointParams.mPath = aPath;
113 0 : mU.mPathPointParams.mDistToPoint = aDistToPoint;
114 :
115 0 : NS_ADDREF(mU.mPathPointParams.mPath); // Retain a reference to path
116 0 : }
117 :
118 : // Copy constructor (NOTE: AddRef's if we're eSegmentType_PathPoint)
119 0 : MotionSegment(const MotionSegment& aOther)
120 : : mRotateType(aOther.mRotateType), mRotateAngle(aOther.mRotateAngle),
121 0 : mSegmentType(aOther.mSegmentType)
122 : {
123 0 : if (mSegmentType == eSegmentType_Translation) {
124 0 : mU.mTranslationParams = aOther.mU.mTranslationParams;
125 : } else { // mSegmentType == eSegmentType_PathPoint
126 0 : mU.mPathPointParams = aOther.mU.mPathPointParams;
127 0 : NS_ADDREF(mU.mPathPointParams.mPath); // Retain a reference to path
128 : }
129 0 : }
130 :
131 : // Destructor (releases any reference we were holding onto)
132 0 : ~MotionSegment()
133 : {
134 0 : if (mSegmentType == eSegmentType_PathPoint) {
135 0 : NS_RELEASE(mU.mPathPointParams.mPath);
136 : }
137 0 : }
138 :
139 : // Comparison operators
140 0 : bool operator==(const MotionSegment& aOther) const
141 : {
142 : // Compare basic params
143 0 : if (mSegmentType != aOther.mSegmentType ||
144 : mRotateType != aOther.mRotateType ||
145 : (mRotateType == eRotateType_Explicit && // Technically, angle mismatch
146 : mRotateAngle != aOther.mRotateAngle)) { // only matters for Explicit.
147 0 : return false;
148 : }
149 :
150 : // Compare translation params, if we're a translation.
151 0 : if (mSegmentType == eSegmentType_Translation) {
152 : return mU.mTranslationParams.mX == aOther.mU.mTranslationParams.mX &&
153 0 : mU.mTranslationParams.mY == aOther.mU.mTranslationParams.mY;
154 : }
155 :
156 : // Else, compare path-point params, if we're a path point.
157 : return (mU.mPathPointParams.mPath == aOther.mU.mPathPointParams.mPath) &&
158 : (mU.mPathPointParams.mDistToPoint ==
159 0 : aOther.mU.mPathPointParams.mDistToPoint);
160 : }
161 :
162 0 : bool operator!=(const MotionSegment& aOther) const
163 : {
164 0 : return !(*this == aOther);
165 : }
166 :
167 : // Member Data
168 : // -----------
169 : RotateType mRotateType; // Explicit angle vs. auto vs. auto-reverse.
170 : float mRotateAngle; // Only used if mRotateType == eRotateType_Explicit.
171 : const SegmentType mSegmentType; // This determines how we interpret
172 : // mU. (const for safety/sanity)
173 :
174 : union { // Union to let us hold the params for either segment-type.
175 : TranslationParams mTranslationParams;
176 : PathPointParams mPathPointParams;
177 : } mU;
178 : };
179 :
180 : typedef nsTArray<MotionSegment> MotionSegmentArray;
181 :
182 : // Helper methods to cast nsSMILValue.mU.mPtr to the right pointer-type
183 : static MotionSegmentArray&
184 0 : ExtractMotionSegmentArray(nsSMILValue& aValue)
185 : {
186 0 : return *static_cast<MotionSegmentArray*>(aValue.mU.mPtr);
187 : }
188 :
189 : static const MotionSegmentArray&
190 0 : ExtractMotionSegmentArray(const nsSMILValue& aValue)
191 : {
192 0 : return *static_cast<const MotionSegmentArray*>(aValue.mU.mPtr);
193 : }
194 :
195 : // nsISMILType Methods
196 : // -------------------
197 :
198 : void
199 0 : SVGMotionSMILType::Init(nsSMILValue& aValue) const
200 : {
201 0 : NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected SMIL type");
202 :
203 0 : aValue.mType = this;
204 0 : aValue.mU.mPtr = new MotionSegmentArray(1);
205 0 : }
206 :
207 : void
208 0 : SVGMotionSMILType::Destroy(nsSMILValue& aValue) const
209 : {
210 0 : NS_ABORT_IF_FALSE(aValue.mType == this, "Unexpected SMIL type");
211 :
212 0 : MotionSegmentArray* arr = static_cast<MotionSegmentArray*>(aValue.mU.mPtr);
213 0 : delete arr;
214 :
215 0 : aValue.mU.mPtr = nsnull;
216 0 : aValue.mType = &nsSMILNullType::sSingleton;
217 0 : }
218 :
219 : nsresult
220 0 : SVGMotionSMILType::Assign(nsSMILValue& aDest, const nsSMILValue& aSrc) const
221 : {
222 0 : NS_ABORT_IF_FALSE(aDest.mType == aSrc.mType, "Incompatible SMIL types");
223 0 : NS_ABORT_IF_FALSE(aDest.mType == this, "Unexpected SMIL type");
224 :
225 0 : const MotionSegmentArray& srcArr = ExtractMotionSegmentArray(aSrc);
226 0 : MotionSegmentArray& dstArr = ExtractMotionSegmentArray(aDest);
227 :
228 : // Ensure we have sufficient memory.
229 0 : if (!dstArr.SetCapacity(srcArr.Length())) {
230 0 : return NS_ERROR_OUT_OF_MEMORY;
231 : }
232 :
233 0 : dstArr = srcArr; // Do the assignment.
234 0 : return NS_OK;
235 : }
236 :
237 : bool
238 0 : SVGMotionSMILType::IsEqual(const nsSMILValue& aLeft,
239 : const nsSMILValue& aRight) const
240 : {
241 0 : NS_ABORT_IF_FALSE(aLeft.mType == aRight.mType, "Incompatible SMIL types");
242 0 : NS_ABORT_IF_FALSE(aLeft.mType == this, "Unexpected SMIL type");
243 :
244 0 : const MotionSegmentArray& leftArr = ExtractMotionSegmentArray(aLeft);
245 0 : const MotionSegmentArray& rightArr = ExtractMotionSegmentArray(aRight);
246 :
247 : // If array-lengths don't match, we're trivially non-equal.
248 0 : if (leftArr.Length() != rightArr.Length()) {
249 0 : return false;
250 : }
251 :
252 : // Array-lengths match -- check each array-entry for equality.
253 0 : PRUint32 length = leftArr.Length(); // == rightArr->Length(), if we get here
254 0 : for (PRUint32 i = 0; i < length; ++i) {
255 0 : if (leftArr[i] != rightArr[i]) {
256 0 : return false;
257 : }
258 : }
259 :
260 0 : return true; // If we get here, we found no differences.
261 : }
262 :
263 : // Helper method for Add & CreateMatrix
264 : inline static void
265 0 : GetAngleAndPointAtDistance(gfxFlattenedPath* aPath, float aDistance,
266 : RotateType aRotateType,
267 : gfxFloat& aRotateAngle, // in & out-param.
268 : gfxPoint& aPoint) // out-param.
269 : {
270 : gfxFloat tangentAngle;
271 : // NOTE: "0.0" below is distance-off-the-path. (see FindPoint documentation)
272 0 : aPoint = aPath->FindPoint(gfxPoint(aDistance, 0.0), &tangentAngle);
273 :
274 : // Update aRotateAngle if it's auto/auto-reverse
275 0 : switch (aRotateType) {
276 : case eRotateType_Explicit:
277 : // Leave aRotateAngle as-is.
278 0 : break;
279 : case eRotateType_Auto:
280 0 : aRotateAngle = tangentAngle;
281 0 : break;
282 : case eRotateType_AutoReverse:
283 0 : aRotateAngle = M_PI + tangentAngle;
284 0 : break;
285 : }
286 0 : }
287 :
288 : nsresult
289 0 : SVGMotionSMILType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
290 : PRUint32 aCount) const
291 : {
292 0 : NS_ABORT_IF_FALSE(aDest.mType == aValueToAdd.mType,
293 : "Incompatible SMIL types");
294 0 : NS_ABORT_IF_FALSE(aDest.mType == this, "Unexpected SMIL type");
295 :
296 0 : MotionSegmentArray& dstArr = ExtractMotionSegmentArray(aDest);
297 0 : const MotionSegmentArray& srcArr = ExtractMotionSegmentArray(aValueToAdd);
298 :
299 : // We're doing a simple add here (as opposed to a sandwich add below). We
300 : // only do this when we're accumulating a repeat result.
301 : // NOTE: In other nsISMILTypes, we use this method with a barely-initialized
302 : // |aDest| value to assist with "by" animation. (In this case,
303 : // "barely-initialized" would mean dstArr.Length() would be empty.) However,
304 : // we don't do this for <animateMotion>, because we instead use our "by"
305 : // value to construct an equivalent "path" attribute, and we use *that* for
306 : // our actual animation.
307 0 : NS_ABORT_IF_FALSE(srcArr.Length() == 1, "Invalid source segment arr to add");
308 0 : NS_ABORT_IF_FALSE(dstArr.Length() == 1, "Invalid dest segment arr to add to");
309 0 : const MotionSegment& srcSeg = srcArr[0];
310 0 : const MotionSegment& dstSeg = dstArr[0];
311 0 : NS_ABORT_IF_FALSE(srcSeg.mSegmentType == eSegmentType_PathPoint,
312 : "expecting to be adding points from a motion path");
313 0 : NS_ABORT_IF_FALSE(dstSeg.mSegmentType == eSegmentType_PathPoint,
314 : "expecting to be adding points from a motion path");
315 :
316 0 : const PathPointParams& srcParams = srcSeg.mU.mPathPointParams;
317 0 : const PathPointParams& dstParams = dstSeg.mU.mPathPointParams;
318 :
319 0 : NS_ABORT_IF_FALSE(srcSeg.mRotateType == dstSeg.mRotateType &&
320 : srcSeg.mRotateAngle == dstSeg.mRotateAngle,
321 : "unexpected angle mismatch");
322 0 : NS_ABORT_IF_FALSE(srcParams.mPath == dstParams.mPath,
323 : "unexpected path mismatch");
324 0 : gfxFlattenedPath* path = srcParams.mPath;
325 :
326 : // Use destination to get our rotate angle.
327 0 : gfxFloat rotateAngle = dstSeg.mRotateAngle;
328 0 : gfxPoint dstPt;
329 : GetAngleAndPointAtDistance(path, dstParams.mDistToPoint, dstSeg.mRotateType,
330 0 : rotateAngle, dstPt);
331 :
332 : // NOTE: "0.0" below is distance-off-the-path. (see FindPoint documentation)
333 0 : gfxPoint srcPt = path->FindPoint(gfxPoint(srcParams.mDistToPoint, 0.0));
334 :
335 0 : float newX = dstPt.x + srcPt.x * aCount;
336 0 : float newY = dstPt.y + srcPt.y * aCount;
337 :
338 : // Replace destination's current value -- a point-on-a-path -- with the
339 : // translation that results from our addition.
340 0 : dstArr.Clear();
341 0 : dstArr.AppendElement(MotionSegment(newX, newY, rotateAngle));
342 0 : return NS_OK;
343 : }
344 :
345 : nsresult
346 0 : SVGMotionSMILType::SandwichAdd(nsSMILValue& aDest,
347 : const nsSMILValue& aValueToAdd) const
348 : {
349 0 : NS_ABORT_IF_FALSE(aDest.mType == aValueToAdd.mType,
350 : "Incompatible SMIL types");
351 0 : NS_ABORT_IF_FALSE(aDest.mType == this, "Unexpected SMIL type");
352 0 : MotionSegmentArray& dstArr = ExtractMotionSegmentArray(aDest);
353 0 : const MotionSegmentArray& srcArr = ExtractMotionSegmentArray(aValueToAdd);
354 :
355 : // We're only expecting to be adding 1 segment on to the list
356 0 : NS_ABORT_IF_FALSE(srcArr.Length() == 1,
357 : "Trying to do sandwich add of more than one value");
358 :
359 0 : if (!dstArr.AppendElement(srcArr[0])) {
360 0 : return NS_ERROR_OUT_OF_MEMORY;
361 : }
362 :
363 0 : return NS_OK;
364 : }
365 :
366 : nsresult
367 0 : SVGMotionSMILType::ComputeDistance(const nsSMILValue& aFrom,
368 : const nsSMILValue& aTo,
369 : double& aDistance) const
370 : {
371 0 : NS_ABORT_IF_FALSE(aFrom.mType == aTo.mType, "Incompatible SMIL types");
372 0 : NS_ABORT_IF_FALSE(aFrom.mType == this, "Unexpected SMIL type");
373 0 : const MotionSegmentArray& fromArr = ExtractMotionSegmentArray(aFrom);
374 0 : const MotionSegmentArray& toArr = ExtractMotionSegmentArray(aTo);
375 :
376 : // ComputeDistance is only used for calculating distances between single
377 : // values in a values array. So we should only have one entry in each array.
378 0 : NS_ABORT_IF_FALSE(fromArr.Length() == 1,
379 : "Wrong number of elements in from value");
380 0 : NS_ABORT_IF_FALSE(toArr.Length() == 1,
381 : "Wrong number of elements in to value");
382 :
383 0 : const MotionSegment& from = fromArr[0];
384 0 : const MotionSegment& to = toArr[0];
385 :
386 0 : NS_ABORT_IF_FALSE(from.mSegmentType == to.mSegmentType,
387 : "Mismatched MotionSegment types");
388 0 : if (from.mSegmentType == eSegmentType_PathPoint) {
389 0 : const PathPointParams& fromParams = from.mU.mPathPointParams;
390 0 : const PathPointParams& toParams = to.mU.mPathPointParams;
391 0 : NS_ABORT_IF_FALSE(fromParams.mPath == toParams.mPath,
392 : "Interpolation endpoints should be from same path");
393 0 : NS_ABORT_IF_FALSE(fromParams.mDistToPoint <= toParams.mDistToPoint,
394 : "To value shouldn't be before from value on path");
395 0 : aDistance = fabs(toParams.mDistToPoint - fromParams.mDistToPoint);
396 : } else {
397 0 : const TranslationParams& fromParams = from.mU.mTranslationParams;
398 0 : const TranslationParams& toParams = to.mU.mTranslationParams;
399 0 : float dX = toParams.mX - fromParams.mX;
400 0 : float dY = toParams.mY - fromParams.mY;
401 0 : aDistance = NS_hypot(dX, dY);
402 : }
403 :
404 0 : return NS_OK;
405 : }
406 :
407 : // Helper method for Interpolate()
408 : static inline float
409 0 : InterpolateFloat(const float& aStartFlt, const float& aEndFlt,
410 : const double& aUnitDistance)
411 : {
412 0 : return aStartFlt + aUnitDistance * (aEndFlt - aStartFlt);
413 : }
414 :
415 : nsresult
416 0 : SVGMotionSMILType::Interpolate(const nsSMILValue& aStartVal,
417 : const nsSMILValue& aEndVal,
418 : double aUnitDistance,
419 : nsSMILValue& aResult) const
420 : {
421 0 : NS_ABORT_IF_FALSE(aStartVal.mType == aEndVal.mType,
422 : "Trying to interpolate different types");
423 0 : NS_ABORT_IF_FALSE(aStartVal.mType == this,
424 : "Unexpected types for interpolation");
425 0 : NS_ABORT_IF_FALSE(aResult.mType == this, "Unexpected result type");
426 0 : NS_ABORT_IF_FALSE(aUnitDistance >= 0.0 && aUnitDistance <= 1.0,
427 : "unit distance value out of bounds");
428 :
429 0 : const MotionSegmentArray& startArr = ExtractMotionSegmentArray(aStartVal);
430 0 : const MotionSegmentArray& endArr = ExtractMotionSegmentArray(aEndVal);
431 0 : MotionSegmentArray& resultArr = ExtractMotionSegmentArray(aResult);
432 :
433 0 : NS_ABORT_IF_FALSE(startArr.Length() <= 1,
434 : "Invalid start-point for animateMotion interpolation");
435 0 : NS_ABORT_IF_FALSE(endArr.Length() == 1,
436 : "Invalid end-point for animateMotion interpolation");
437 0 : NS_ABORT_IF_FALSE(resultArr.IsEmpty(),
438 : "Expecting result to be just-initialized w/ empty array");
439 :
440 0 : const MotionSegment& endSeg = endArr[0];
441 0 : NS_ABORT_IF_FALSE(endSeg.mSegmentType == eSegmentType_PathPoint,
442 : "Expecting to be interpolating along a path");
443 :
444 0 : const PathPointParams& endParams = endSeg.mU.mPathPointParams;
445 : // NOTE: path & angle should match between start & end (since presumably
446 : // start & end came from the same <animateMotion> element), unless start is
447 : // empty. (as it would be for pure 'to' animation)
448 0 : gfxFlattenedPath* path = endParams.mPath;
449 0 : RotateType rotateType = endSeg.mRotateType;
450 0 : float rotateAngle = endSeg.mRotateAngle;
451 :
452 : float startDist;
453 0 : if (startArr.IsEmpty()) {
454 0 : startDist = 0.0f;
455 : } else {
456 0 : const MotionSegment& startSeg = startArr[0];
457 0 : NS_ABORT_IF_FALSE(startSeg.mSegmentType == eSegmentType_PathPoint,
458 : "Expecting to be interpolating along a path");
459 0 : const PathPointParams& startParams = startSeg.mU.mPathPointParams;
460 0 : NS_ABORT_IF_FALSE(startSeg.mRotateType == endSeg.mRotateType &&
461 : startSeg.mRotateAngle == endSeg.mRotateAngle,
462 : "unexpected angle mismatch");
463 0 : NS_ABORT_IF_FALSE(startParams.mPath == endParams.mPath,
464 : "unexpected path mismatch");
465 0 : startDist = startParams.mDistToPoint;
466 : }
467 :
468 : // Get the interpolated distance along our path.
469 : float resultDist = InterpolateFloat(startDist, endParams.mDistToPoint,
470 0 : aUnitDistance);
471 :
472 : // Construct the intermediate result segment, and put it in our outparam.
473 : // AppendElement has guaranteed success here, since Init() allocates 1 slot.
474 : resultArr.AppendElement(MotionSegment(path, resultDist,
475 0 : rotateType, rotateAngle));
476 0 : return NS_OK;
477 : }
478 :
479 : /* static */ gfxMatrix
480 0 : SVGMotionSMILType::CreateMatrix(const nsSMILValue& aSMILVal)
481 : {
482 0 : const MotionSegmentArray& arr = ExtractMotionSegmentArray(aSMILVal);
483 :
484 0 : gfxMatrix matrix;
485 0 : PRUint32 length = arr.Length();
486 0 : for (PRUint32 i = 0; i < length; i++) {
487 0 : gfxPoint point; // initialized below
488 0 : gfxFloat rotateAngle = arr[i].mRotateAngle; // might get updated below
489 0 : if (arr[i].mSegmentType == eSegmentType_Translation) {
490 0 : point.x = arr[i].mU.mTranslationParams.mX;
491 0 : point.y = arr[i].mU.mTranslationParams.mY;
492 0 : NS_ABORT_IF_FALSE(arr[i].mRotateType == eRotateType_Explicit,
493 : "'auto'/'auto-reverse' should have been converted to "
494 : "explicit angles when we generated this translation");
495 : } else {
496 0 : GetAngleAndPointAtDistance(arr[i].mU.mPathPointParams.mPath,
497 0 : arr[i].mU.mPathPointParams.mDistToPoint,
498 0 : arr[i].mRotateType,
499 0 : rotateAngle, point);
500 : }
501 0 : matrix.Translate(point);
502 0 : matrix.Rotate(rotateAngle);
503 : }
504 : return matrix;
505 : }
506 :
507 : /* static */ nsSMILValue
508 0 : SVGMotionSMILType::ConstructSMILValue(gfxFlattenedPath* aPath,
509 : float aDist,
510 : RotateType aRotateType,
511 : float aRotateAngle)
512 : {
513 0 : nsSMILValue smilVal(&SVGMotionSMILType::sSingleton);
514 0 : MotionSegmentArray& arr = ExtractMotionSegmentArray(smilVal);
515 :
516 : // AppendElement has guaranteed success here, since Init() allocates 1 slot.
517 0 : arr.AppendElement(MotionSegment(aPath, aDist, aRotateType, aRotateAngle));
518 : return smilVal;
519 : }
520 :
521 4392 : } // namespace mozilla
|