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 "SVGPathSegListSMILType.h"
38 : #include "nsSMILValue.h"
39 : #include "SVGPathSegUtils.h"
40 : #include "SVGPathData.h"
41 : #include "mozilla/Util.h"
42 : #include <math.h>
43 :
44 : // Indices of boolean flags within 'arc' segment chunks in path-data arrays
45 : // (where '0' would correspond to the index of the encoded segment type):
46 : #define LARGE_ARC_FLAG_IDX 4
47 : #define SWEEP_FLAG_IDX 5
48 :
49 : namespace mozilla {
50 :
51 1464 : /*static*/ SVGPathSegListSMILType SVGPathSegListSMILType::sSingleton;
52 :
53 : //----------------------------------------------------------------------
54 : // nsISMILType implementation
55 :
56 : void
57 0 : SVGPathSegListSMILType::Init(nsSMILValue &aValue) const
58 : {
59 0 : NS_ABORT_IF_FALSE(aValue.IsNull(), "Unexpected value type");
60 0 : aValue.mU.mPtr = new SVGPathDataAndOwner();
61 0 : aValue.mType = this;
62 0 : }
63 :
64 : void
65 0 : SVGPathSegListSMILType::Destroy(nsSMILValue& aValue) const
66 : {
67 0 : NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type");
68 0 : delete static_cast<SVGPathDataAndOwner*>(aValue.mU.mPtr);
69 0 : aValue.mU.mPtr = nsnull;
70 0 : aValue.mType = &nsSMILNullType::sSingleton;
71 0 : }
72 :
73 : nsresult
74 0 : SVGPathSegListSMILType::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 SVGPathDataAndOwner* src =
81 0 : static_cast<const SVGPathDataAndOwner*>(aSrc.mU.mPtr);
82 : SVGPathDataAndOwner* dest =
83 0 : static_cast<SVGPathDataAndOwner*>(aDest.mU.mPtr);
84 :
85 0 : return dest->CopyFrom(*src);
86 : }
87 :
88 : bool
89 0 : SVGPathSegListSMILType::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 SVGPathDataAndOwner*>(aLeft.mU.mPtr) ==
96 0 : *static_cast<const SVGPathDataAndOwner*>(aRight.mU.mPtr);
97 : }
98 :
99 : static bool
100 0 : ArcFlagsDiffer(SVGPathDataAndOwner::const_iterator aPathData1,
101 : SVGPathDataAndOwner::const_iterator aPathData2)
102 : {
103 0 : NS_ABORT_IF_FALSE
104 : (SVGPathSegUtils::IsArcType(SVGPathSegUtils::DecodeType(aPathData1[0])),
105 : "ArcFlagsDiffer called with non-arc segment");
106 0 : NS_ABORT_IF_FALSE
107 : (SVGPathSegUtils::IsArcType(SVGPathSegUtils::DecodeType(aPathData2[0])),
108 : "ArcFlagsDiffer called with non-arc segment");
109 :
110 0 : return aPathData1[LARGE_ARC_FLAG_IDX] != aPathData2[LARGE_ARC_FLAG_IDX] ||
111 0 : aPathData1[SWEEP_FLAG_IDX] != aPathData2[SWEEP_FLAG_IDX];
112 : }
113 :
114 : enum PathInterpolationResult {
115 : eCannotInterpolate,
116 : eRequiresConversion,
117 : eCanInterpolate
118 : };
119 :
120 : static PathInterpolationResult
121 0 : CanInterpolate(const SVGPathDataAndOwner& aStart,
122 : const SVGPathDataAndOwner& aEnd)
123 : {
124 0 : if (aStart.IsIdentity()) {
125 0 : return eCanInterpolate;
126 : }
127 :
128 0 : if (aStart.Length() != aEnd.Length()) {
129 0 : return eCannotInterpolate;
130 : }
131 :
132 0 : PathInterpolationResult result = eCanInterpolate;
133 :
134 0 : SVGPathDataAndOwner::const_iterator pStart = aStart.begin();
135 0 : SVGPathDataAndOwner::const_iterator pEnd = aEnd.begin();
136 0 : SVGPathDataAndOwner::const_iterator pStartDataEnd = aStart.end();
137 0 : SVGPathDataAndOwner::const_iterator pEndDataEnd = aEnd.end();
138 :
139 0 : while (pStart < pStartDataEnd && pEnd < pEndDataEnd) {
140 0 : PRUint32 startType = SVGPathSegUtils::DecodeType(*pStart);
141 0 : PRUint32 endType = SVGPathSegUtils::DecodeType(*pEnd);
142 :
143 0 : if (SVGPathSegUtils::IsArcType(startType) &&
144 0 : SVGPathSegUtils::IsArcType(endType) &&
145 0 : ArcFlagsDiffer(pStart, pEnd)) {
146 0 : return eCannotInterpolate;
147 : }
148 :
149 0 : if (startType != endType) {
150 0 : if (!SVGPathSegUtils::SameTypeModuloRelativeness(startType, endType)) {
151 0 : return eCannotInterpolate;
152 : }
153 :
154 0 : result = eRequiresConversion;
155 : }
156 :
157 0 : pStart += 1 + SVGPathSegUtils::ArgCountForType(startType);
158 0 : pEnd += 1 + SVGPathSegUtils::ArgCountForType(endType);
159 : }
160 :
161 0 : NS_ABORT_IF_FALSE(pStart <= pStartDataEnd && pEnd <= pEndDataEnd,
162 : "Iterated past end of buffer! (Corrupt path data?)");
163 :
164 0 : if (pStart != pStartDataEnd || pEnd != pEndDataEnd) {
165 0 : return eCannotInterpolate;
166 : }
167 :
168 0 : return result;
169 : }
170 :
171 : enum RelativenessAdjustmentType {
172 : eAbsoluteToRelative,
173 : eRelativeToAbsolute
174 : };
175 :
176 : static inline void
177 0 : AdjustSegmentForRelativeness(RelativenessAdjustmentType aAdjustmentType,
178 : const SVGPathDataAndOwner::iterator& aSegmentToAdjust,
179 : const SVGPathTraversalState& aState)
180 : {
181 0 : if (aAdjustmentType == eAbsoluteToRelative) {
182 0 : aSegmentToAdjust[0] -= aState.pos.x;
183 0 : aSegmentToAdjust[1] -= aState.pos.y;
184 : } else {
185 0 : aSegmentToAdjust[0] += aState.pos.x;
186 0 : aSegmentToAdjust[1] += aState.pos.y;
187 : }
188 0 : }
189 :
190 : /**
191 : * Helper function for AddWeightedPathSegLists, to add multiples of two
192 : * path-segments of the same type.
193 : *
194 : * NOTE: |aSeg1| is allowed to be nsnull, so we use |aSeg2| as the
195 : * authoritative source of things like segment-type and boolean arc flags.
196 : *
197 : * @param aCoeff1 The coefficient to use on the first segment.
198 : * @param aSeg1 An iterator pointing to the first segment. This can be
199 : * null, which is treated as identity (zero).
200 : * @param aCoeff2 The coefficient to use on the second segment.
201 : * @param aSeg2 An iterator pointing to the second segment.
202 : * @param [out] aResultSeg An iterator pointing to where we should write the
203 : * result of this operation.
204 : */
205 : static inline void
206 0 : AddWeightedPathSegs(double aCoeff1,
207 : SVGPathDataAndOwner::const_iterator& aSeg1,
208 : double aCoeff2,
209 : SVGPathDataAndOwner::const_iterator& aSeg2,
210 : SVGPathDataAndOwner::iterator& aResultSeg)
211 : {
212 0 : NS_ABORT_IF_FALSE(aSeg2, "2nd segment must be non-null");
213 0 : NS_ABORT_IF_FALSE(aResultSeg, "result segment must be non-null");
214 :
215 0 : PRUint32 segType = SVGPathSegUtils::DecodeType(aSeg2[0]);
216 0 : NS_ABORT_IF_FALSE(!aSeg1 || SVGPathSegUtils::DecodeType(*aSeg1) == segType,
217 : "unexpected segment type");
218 :
219 : // FIRST: Directly copy the arguments that don't make sense to add.
220 0 : aResultSeg[0] = aSeg2[0]; // encoded segment type
221 :
222 0 : bool isArcType = SVGPathSegUtils::IsArcType(segType);
223 0 : if (isArcType) {
224 : // Copy boolean arc flags.
225 0 : NS_ABORT_IF_FALSE(!aSeg1 || !ArcFlagsDiffer(aSeg1, aSeg2),
226 : "Expecting arc flags to match");
227 0 : aResultSeg[LARGE_ARC_FLAG_IDX] = aSeg2[LARGE_ARC_FLAG_IDX];
228 0 : aResultSeg[SWEEP_FLAG_IDX] = aSeg2[SWEEP_FLAG_IDX];
229 : }
230 :
231 : // SECOND: Add the arguments that are supposed to be added.
232 : // (The 1's below are to account for segment type)
233 0 : PRUint32 numArgs = SVGPathSegUtils::ArgCountForType(segType);
234 0 : for (PRUint32 i = 1; i < 1 + numArgs; ++i) {
235 : // Need to skip arc flags for arc-type segments. (already handled them)
236 0 : if (!(isArcType && (i == LARGE_ARC_FLAG_IDX || i == SWEEP_FLAG_IDX))) {
237 0 : aResultSeg[i] = (aSeg1 ? aCoeff1 * aSeg1[i] : 0.0) + aCoeff2 * aSeg2[i];
238 : }
239 : }
240 :
241 : // FINALLY: Shift iterators forward. ("1+" is to include seg-type)
242 0 : if (aSeg1) {
243 0 : aSeg1 += 1 + numArgs;
244 : }
245 0 : aSeg2 += 1 + numArgs;
246 0 : aResultSeg += 1 + numArgs;
247 0 : }
248 :
249 : /**
250 : * Helper function for Add & Interpolate, to add multiples of two path-segment
251 : * lists.
252 : *
253 : * NOTE: aList1 and aList2 are assumed to have their segment-types and
254 : * segment-count match exactly (unless aList1 is an identity value).
255 : *
256 : * NOTE: aResult, the output list, is expected to either be an identity value
257 : * (in which case we'll grow it) *or* to already have the exactly right length
258 : * (e.g. in cases where aList1 and aResult are actually the same list).
259 : *
260 : * @param aCoeff1 The coefficient to use on the first path segment list.
261 : * @param aList1 The first path segment list. Allowed to be identity.
262 : * @param aCoeff2 The coefficient to use on the second path segment list.
263 : * @param aList2 The second path segment list.
264 : * @param [out] aResultSeg The resulting path segment list. Allowed to be
265 : * identity, in which case we'll grow it to the right
266 : * size. Also allowed to be the same list as aList1.
267 : */
268 : static void
269 0 : AddWeightedPathSegLists(double aCoeff1, const SVGPathDataAndOwner& aList1,
270 : double aCoeff2, const SVGPathDataAndOwner& aList2,
271 : SVGPathDataAndOwner& aResult)
272 : {
273 0 : NS_ABORT_IF_FALSE(aCoeff1 >= 0.0 && aCoeff2 >= 0.0,
274 : "expecting non-negative coefficients");
275 0 : NS_ABORT_IF_FALSE(!aList2.IsIdentity(),
276 : "expecting 2nd list to be non-identity");
277 0 : NS_ABORT_IF_FALSE(aList1.IsIdentity() || aList1.Length() == aList2.Length(),
278 : "expecting 1st list to be identity or to have same "
279 : "length as 2nd list");
280 0 : NS_ABORT_IF_FALSE(aResult.IsIdentity() || aResult.Length() == aList2.Length(),
281 : "expecting result list to be identity or to have same "
282 : "length as 2nd list");
283 :
284 : SVGPathDataAndOwner::const_iterator iter1, end1;
285 0 : if (aList1.IsIdentity()) {
286 0 : iter1 = end1 = nsnull; // indicate that this is an identity list
287 : } else {
288 0 : iter1 = aList1.begin();
289 0 : end1 = aList1.end();
290 : }
291 0 : SVGPathDataAndOwner::const_iterator iter2 = aList2.begin();
292 0 : SVGPathDataAndOwner::const_iterator end2 = aList2.end();
293 :
294 : // Grow |aResult| if necessary. (NOTE: It's possible that aResult and aList1
295 : // are the same list, so this may implicitly resize aList1. That's fine,
296 : // because in that case, we will have already set iter1 to nsnull above, to
297 : // record that our first operand is an identity value.)
298 0 : if (aResult.IsIdentity()) {
299 0 : DebugOnly<bool> success = aResult.SetLength(aList2.Length());
300 0 : NS_ABORT_IF_FALSE(success, "infallible nsTArray::SetLength should succeed");
301 0 : aResult.SetElement(aList2.Element()); // propagate target element info!
302 : }
303 :
304 0 : SVGPathDataAndOwner::iterator resultIter = aResult.begin();
305 :
306 0 : while ((!iter1 || iter1 != end1) &&
307 : iter2 != end2) {
308 : AddWeightedPathSegs(aCoeff1, iter1,
309 : aCoeff2, iter2,
310 0 : resultIter);
311 : }
312 0 : NS_ABORT_IF_FALSE((!iter1 || iter1 == end1) &&
313 : iter2 == end2 &&
314 : resultIter == aResult.end(),
315 : "Very, very bad - path data corrupt");
316 0 : }
317 :
318 : static void
319 0 : ConvertPathSegmentData(SVGPathDataAndOwner::const_iterator& aStart,
320 : SVGPathDataAndOwner::const_iterator& aEnd,
321 : SVGPathDataAndOwner::iterator& aResult,
322 : SVGPathTraversalState& aState)
323 : {
324 0 : PRUint32 startType = SVGPathSegUtils::DecodeType(*aStart);
325 0 : PRUint32 endType = SVGPathSegUtils::DecodeType(*aEnd);
326 :
327 : PRUint32 segmentLengthIncludingType =
328 0 : 1 + SVGPathSegUtils::ArgCountForType(startType);
329 :
330 0 : SVGPathDataAndOwner::const_iterator pResultSegmentBegin = aResult;
331 :
332 0 : if (startType == endType) {
333 : // No conversion need, just directly copy aStart.
334 0 : aEnd += segmentLengthIncludingType;
335 0 : while (segmentLengthIncludingType) {
336 0 : *aResult++ = *aStart++;
337 0 : --segmentLengthIncludingType;
338 : }
339 0 : SVGPathSegUtils::TraversePathSegment(pResultSegmentBegin, aState);
340 0 : return;
341 : }
342 :
343 0 : NS_ABORT_IF_FALSE
344 : (SVGPathSegUtils::SameTypeModuloRelativeness(startType, endType),
345 : "Incompatible path segment types passed to ConvertPathSegmentData!");
346 :
347 : RelativenessAdjustmentType adjustmentType =
348 0 : SVGPathSegUtils::IsRelativeType(startType) ? eRelativeToAbsolute
349 0 : : eAbsoluteToRelative;
350 :
351 0 : NS_ABORT_IF_FALSE
352 : (segmentLengthIncludingType ==
353 : 1 + SVGPathSegUtils::ArgCountForType(endType),
354 : "Compatible path segment types for interpolation had different lengths!");
355 :
356 0 : aResult[0] = aEnd[0];
357 :
358 0 : switch (endType) {
359 : case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
360 : case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
361 0 : aResult[1] = aStart[1] +
362 0 : (adjustmentType == eRelativeToAbsolute ? 1 : -1) * aState.pos.x;
363 0 : break;
364 : case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
365 : case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
366 0 : aResult[1] = aStart[1] +
367 0 : (adjustmentType == eRelativeToAbsolute ? 1 : -1) * aState.pos.y;
368 0 : break;
369 : case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
370 : case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
371 0 : aResult[1] = aStart[1];
372 0 : aResult[2] = aStart[2];
373 0 : aResult[3] = aStart[3];
374 0 : aResult[4] = aStart[4];
375 0 : aResult[5] = aStart[5];
376 0 : aResult[6] = aStart[6];
377 0 : aResult[7] = aStart[7];
378 0 : AdjustSegmentForRelativeness(adjustmentType, aResult + 6, aState);
379 0 : break;
380 : case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
381 : case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
382 0 : aResult[5] = aStart[5];
383 0 : aResult[6] = aStart[6];
384 0 : AdjustSegmentForRelativeness(adjustmentType, aResult + 5, aState);
385 : // fall through
386 : case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
387 : case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
388 : case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
389 : case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
390 0 : aResult[3] = aStart[3];
391 0 : aResult[4] = aStart[4];
392 0 : AdjustSegmentForRelativeness(adjustmentType, aResult + 3, aState);
393 : // fall through
394 : case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
395 : case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
396 : case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
397 : case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
398 : case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
399 : case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
400 0 : aResult[1] = aStart[1];
401 0 : aResult[2] = aStart[2];
402 0 : AdjustSegmentForRelativeness(adjustmentType, aResult + 1, aState);
403 0 : break;
404 : }
405 :
406 0 : SVGPathSegUtils::TraversePathSegment(pResultSegmentBegin, aState);
407 0 : aStart += segmentLengthIncludingType;
408 0 : aEnd += segmentLengthIncludingType;
409 0 : aResult += segmentLengthIncludingType;
410 : }
411 :
412 : static void
413 0 : ConvertAllPathSegmentData(SVGPathDataAndOwner::const_iterator aStart,
414 : SVGPathDataAndOwner::const_iterator aStartDataEnd,
415 : SVGPathDataAndOwner::const_iterator aEnd,
416 : SVGPathDataAndOwner::const_iterator aEndDataEnd,
417 : SVGPathDataAndOwner::iterator aResult)
418 : {
419 0 : SVGPathTraversalState state;
420 0 : state.mode = SVGPathTraversalState::eUpdateOnlyStartAndCurrentPos;
421 0 : while (aStart < aStartDataEnd && aEnd < aEndDataEnd) {
422 0 : ConvertPathSegmentData(aStart, aEnd, aResult, state);
423 : }
424 0 : NS_ABORT_IF_FALSE(aStart == aStartDataEnd && aEnd == aEndDataEnd,
425 : "Failed to convert all path segment data! (Corrupt?)");
426 0 : }
427 :
428 : nsresult
429 0 : SVGPathSegListSMILType::Add(nsSMILValue& aDest,
430 : const nsSMILValue& aValueToAdd,
431 : PRUint32 aCount) const
432 : {
433 0 : NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type");
434 0 : NS_PRECONDITION(aValueToAdd.mType == this, "Incompatible SMIL type");
435 :
436 : SVGPathDataAndOwner& dest =
437 0 : *static_cast<SVGPathDataAndOwner*>(aDest.mU.mPtr);
438 : const SVGPathDataAndOwner& valueToAdd =
439 0 : *static_cast<const SVGPathDataAndOwner*>(aValueToAdd.mU.mPtr);
440 :
441 0 : if (valueToAdd.IsIdentity()) { // Adding identity value - no-op
442 0 : return NS_OK;
443 : }
444 :
445 0 : if (!dest.IsIdentity()) {
446 : // Neither value is identity; make sure they're compatible.
447 0 : NS_ABORT_IF_FALSE(dest.Element() == valueToAdd.Element(),
448 : "adding values from different elements...?");
449 :
450 0 : PathInterpolationResult check = CanInterpolate(dest, valueToAdd);
451 0 : if (check == eCannotInterpolate) {
452 : // nsSVGUtils::ReportToConsole - can't add path segment lists with
453 : // different numbers of segments, with arcs that have different flag
454 : // values, or with incompatible segment types.
455 0 : return NS_ERROR_FAILURE;
456 : }
457 0 : if (check == eRequiresConversion) {
458 : // Convert dest, in-place, to match the types in valueToAdd:
459 0 : ConvertAllPathSegmentData(dest.begin(), dest.end(),
460 : valueToAdd.begin(), valueToAdd.end(),
461 0 : dest.begin());
462 : }
463 : }
464 :
465 0 : AddWeightedPathSegLists(1.0, dest, aCount, valueToAdd, dest);
466 :
467 0 : return NS_OK;
468 : }
469 :
470 : nsresult
471 0 : SVGPathSegListSMILType::ComputeDistance(const nsSMILValue& aFrom,
472 : const nsSMILValue& aTo,
473 : double& aDistance) const
474 : {
475 0 : NS_PRECONDITION(aFrom.mType == this, "Unexpected SMIL type");
476 0 : NS_PRECONDITION(aTo.mType == this, "Incompatible SMIL type");
477 :
478 : // See https://bugzilla.mozilla.org/show_bug.cgi?id=522306#c18
479 :
480 : // nsSVGUtils::ReportToConsole
481 0 : return NS_ERROR_NOT_IMPLEMENTED;
482 : }
483 :
484 : nsresult
485 0 : SVGPathSegListSMILType::Interpolate(const nsSMILValue& aStartVal,
486 : const nsSMILValue& aEndVal,
487 : double aUnitDistance,
488 : nsSMILValue& aResult) const
489 : {
490 0 : NS_PRECONDITION(aStartVal.mType == aEndVal.mType,
491 : "Trying to interpolate different types");
492 0 : NS_PRECONDITION(aStartVal.mType == this,
493 : "Unexpected types for interpolation");
494 0 : NS_PRECONDITION(aResult.mType == this, "Unexpected result type");
495 :
496 : const SVGPathDataAndOwner& start =
497 0 : *static_cast<const SVGPathDataAndOwner*>(aStartVal.mU.mPtr);
498 : const SVGPathDataAndOwner& end =
499 0 : *static_cast<const SVGPathDataAndOwner*>(aEndVal.mU.mPtr);
500 : SVGPathDataAndOwner& result =
501 0 : *static_cast<SVGPathDataAndOwner*>(aResult.mU.mPtr);
502 0 : NS_ABORT_IF_FALSE(result.IsIdentity(),
503 : "expecting outparam to start out as identity");
504 :
505 0 : PathInterpolationResult check = CanInterpolate(start, end);
506 :
507 0 : if (check == eCannotInterpolate) {
508 : // nsSVGUtils::ReportToConsole - can't interpolate path segment lists with
509 : // different numbers of segments, with arcs with different flag values, or
510 : // with incompatible segment types.
511 0 : return NS_ERROR_FAILURE;
512 : }
513 :
514 0 : const SVGPathDataAndOwner* startListToUse = &start;
515 0 : if (check == eRequiresConversion) {
516 : // Can't convert |start| in-place, since it's const. Instead, we copy it
517 : // into |result|, converting the types as we go, and use that as our start.
518 0 : DebugOnly<bool> success = result.SetLength(end.Length());
519 0 : NS_ABORT_IF_FALSE(success, "infallible nsTArray::SetLength should succeed");
520 0 : result.SetElement(end.Element()); // propagate target element info!
521 :
522 : ConvertAllPathSegmentData(start.begin(), start.end(),
523 : end.begin(), end.end(),
524 0 : result.begin());
525 0 : startListToUse = &result;
526 : }
527 :
528 : AddWeightedPathSegLists(1.0 - aUnitDistance, *startListToUse,
529 0 : aUnitDistance, end, result);
530 :
531 0 : return NS_OK;
532 : }
533 :
534 4392 : } // namespace mozilla
|