static PathInterpolationResult CanInterpolate(const SVGPathDataAndInfo& aStart, const SVGPathDataAndInfo& aEnd) { if (aStart.IsIdentity()) { return eCanInterpolate; } if (aStart.Length() != aEnd.Length()) { return eCannotInterpolate; } PathInterpolationResult result = eCanInterpolate; SVGPathDataAndInfo::const_iterator pStart = aStart.begin(); SVGPathDataAndInfo::const_iterator pEnd = aEnd.begin(); SVGPathDataAndInfo::const_iterator pStartDataEnd = aStart.end(); SVGPathDataAndInfo::const_iterator pEndDataEnd = aEnd.end(); while (pStart < pStartDataEnd && pEnd < pEndDataEnd) { uint32_t startType = SVGPathSegUtils::DecodeType(*pStart); uint32_t endType = SVGPathSegUtils::DecodeType(*pEnd); if (SVGPathSegUtils::IsArcType(startType) && SVGPathSegUtils::IsArcType(endType) && ArcFlagsDiffer(pStart, pEnd)) { return eCannotInterpolate; } if (startType != endType) { if (!SVGPathSegUtils::SameTypeModuloRelativeness(startType, endType)) { return eCannotInterpolate; } result = eRequiresConversion; } pStart += 1 + SVGPathSegUtils::ArgCountForType(startType); pEnd += 1 + SVGPathSegUtils::ArgCountForType(endType); } MOZ_ASSERT(pStart <= pStartDataEnd && pEnd <= pEndDataEnd, "Iterated past end of buffer! (Corrupt path data?)"); if (pStart != pStartDataEnd || pEnd != pEndDataEnd) { return eCannotInterpolate; } return result; }
/** * Helper function for Add & Interpolate, to add multiples of two path-segment * lists. * * NOTE: aList1 and aList2 are assumed to have their segment-types and * segment-count match exactly (unless aList1 is an identity value). * * NOTE: aResult, the output list, is expected to either be an identity value * (in which case we'll grow it) *or* to already have the exactly right length * (e.g. in cases where aList1 and aResult are actually the same list). * * @param aCoeff1 The coefficient to use on the first path segment list. * @param aList1 The first path segment list. Allowed to be identity. * @param aCoeff2 The coefficient to use on the second path segment list. * @param aList2 The second path segment list. * @param [out] aResultSeg The resulting path segment list. Allowed to be * identity, in which case we'll grow it to the right * size. Also allowed to be the same list as aList1. */ static nsresult AddWeightedPathSegLists(double aCoeff1, const SVGPathDataAndInfo& aList1, double aCoeff2, const SVGPathDataAndInfo& aList2, SVGPathDataAndInfo& aResult) { MOZ_ASSERT(aCoeff1 >= 0.0 && aCoeff2 >= 0.0, "expecting non-negative coefficients"); MOZ_ASSERT(!aList2.IsIdentity(), "expecting 2nd list to be non-identity"); MOZ_ASSERT(aList1.IsIdentity() || aList1.Length() == aList2.Length(), "expecting 1st list to be identity or to have same " "length as 2nd list"); MOZ_ASSERT(aResult.IsIdentity() || aResult.Length() == aList2.Length(), "expecting result list to be identity or to have same " "length as 2nd list"); SVGPathDataAndInfo::const_iterator iter1, end1; if (aList1.IsIdentity()) { iter1 = end1 = nullptr; // indicate that this is an identity list } else { iter1 = aList1.begin(); end1 = aList1.end(); } SVGPathDataAndInfo::const_iterator iter2 = aList2.begin(); SVGPathDataAndInfo::const_iterator end2 = aList2.end(); // Grow |aResult| if necessary. (NOTE: It's possible that aResult and aList1 // are the same list, so this may implicitly resize aList1. That's fine, // because in that case, we will have already set iter1 to nullptr above, to // record that our first operand is an identity value.) if (aResult.IsIdentity()) { if (!aResult.SetLength(aList2.Length())) { return NS_ERROR_OUT_OF_MEMORY; } aResult.SetElement(aList2.Element()); // propagate target element info! } SVGPathDataAndInfo::iterator resultIter = aResult.begin(); while ((!iter1 || iter1 != end1) && iter2 != end2) { AddWeightedPathSegs(aCoeff1, iter1, aCoeff2, iter2, resultIter); } MOZ_ASSERT( (!iter1 || iter1 == end1) && iter2 == end2 && resultIter == aResult.end(), "Very, very bad - path data corrupt"); return NS_OK; }