diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/svg/SVGPathSegListSMILType.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/svg/SVGPathSegListSMILType.cpp')
-rw-r--r-- | dom/svg/SVGPathSegListSMILType.cpp | 501 |
1 files changed, 501 insertions, 0 deletions
diff --git a/dom/svg/SVGPathSegListSMILType.cpp b/dom/svg/SVGPathSegListSMILType.cpp new file mode 100644 index 000000000..845781f9f --- /dev/null +++ b/dom/svg/SVGPathSegListSMILType.cpp @@ -0,0 +1,501 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/DebugOnly.h" + +#include "SVGPathSegListSMILType.h" +#include "nsSMILValue.h" +#include "SVGPathSegUtils.h" +#include "SVGPathData.h" + +// Indices of boolean flags within 'arc' segment chunks in path-data arrays +// (where '0' would correspond to the index of the encoded segment type): +#define LARGE_ARC_FLAG_IDX 4 +#define SWEEP_FLAG_IDX 5 + +namespace mozilla { + +//---------------------------------------------------------------------- +// nsISMILType implementation + +void +SVGPathSegListSMILType::Init(nsSMILValue &aValue) const +{ + MOZ_ASSERT(aValue.IsNull(), "Unexpected value type"); + aValue.mU.mPtr = new SVGPathDataAndInfo(); + aValue.mType = this; +} + +void +SVGPathSegListSMILType::Destroy(nsSMILValue& aValue) const +{ + NS_PRECONDITION(aValue.mType == this, "Unexpected SMIL value type"); + delete static_cast<SVGPathDataAndInfo*>(aValue.mU.mPtr); + aValue.mU.mPtr = nullptr; + aValue.mType = nsSMILNullType::Singleton(); +} + +nsresult +SVGPathSegListSMILType::Assign(nsSMILValue& aDest, + const nsSMILValue& aSrc) const +{ + NS_PRECONDITION(aDest.mType == aSrc.mType, "Incompatible SMIL types"); + NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL value"); + + const SVGPathDataAndInfo* src = + static_cast<const SVGPathDataAndInfo*>(aSrc.mU.mPtr); + SVGPathDataAndInfo* dest = + static_cast<SVGPathDataAndInfo*>(aDest.mU.mPtr); + + return dest->CopyFrom(*src); +} + +bool +SVGPathSegListSMILType::IsEqual(const nsSMILValue& aLeft, + const nsSMILValue& aRight) const +{ + NS_PRECONDITION(aLeft.mType == aRight.mType, "Incompatible SMIL types"); + NS_PRECONDITION(aLeft.mType == this, "Unexpected type for SMIL value"); + + return *static_cast<const SVGPathDataAndInfo*>(aLeft.mU.mPtr) == + *static_cast<const SVGPathDataAndInfo*>(aRight.mU.mPtr); +} + +static bool +ArcFlagsDiffer(SVGPathDataAndInfo::const_iterator aPathData1, + SVGPathDataAndInfo::const_iterator aPathData2) +{ + MOZ_ASSERT + (SVGPathSegUtils::IsArcType(SVGPathSegUtils::DecodeType(aPathData1[0])), + "ArcFlagsDiffer called with non-arc segment"); + MOZ_ASSERT + (SVGPathSegUtils::IsArcType(SVGPathSegUtils::DecodeType(aPathData2[0])), + "ArcFlagsDiffer called with non-arc segment"); + + return aPathData1[LARGE_ARC_FLAG_IDX] != aPathData2[LARGE_ARC_FLAG_IDX] || + aPathData1[SWEEP_FLAG_IDX] != aPathData2[SWEEP_FLAG_IDX]; +} + +enum PathInterpolationResult { + eCannotInterpolate, + eRequiresConversion, + eCanInterpolate +}; + +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; +} + +enum RelativenessAdjustmentType { + eAbsoluteToRelative, + eRelativeToAbsolute +}; + +static inline void +AdjustSegmentForRelativeness(RelativenessAdjustmentType aAdjustmentType, + const SVGPathDataAndInfo::iterator& aSegmentToAdjust, + const SVGPathTraversalState& aState) +{ + if (aAdjustmentType == eAbsoluteToRelative) { + aSegmentToAdjust[0] -= aState.pos.x; + aSegmentToAdjust[1] -= aState.pos.y; + } else { + aSegmentToAdjust[0] += aState.pos.x; + aSegmentToAdjust[1] += aState.pos.y; + } +} + +/** + * Helper function for AddWeightedPathSegLists, to add multiples of two + * path-segments of the same type. + * + * NOTE: |aSeg1| is allowed to be nullptr, so we use |aSeg2| as the + * authoritative source of things like segment-type and boolean arc flags. + * + * @param aCoeff1 The coefficient to use on the first segment. + * @param aSeg1 An iterator pointing to the first segment. This can be + * null, which is treated as identity (zero). + * @param aCoeff2 The coefficient to use on the second segment. + * @param aSeg2 An iterator pointing to the second segment. + * @param [out] aResultSeg An iterator pointing to where we should write the + * result of this operation. + */ +static inline void +AddWeightedPathSegs(double aCoeff1, + SVGPathDataAndInfo::const_iterator& aSeg1, + double aCoeff2, + SVGPathDataAndInfo::const_iterator& aSeg2, + SVGPathDataAndInfo::iterator& aResultSeg) +{ + MOZ_ASSERT(aSeg2, "2nd segment must be non-null"); + MOZ_ASSERT(aResultSeg, "result segment must be non-null"); + + uint32_t segType = SVGPathSegUtils::DecodeType(aSeg2[0]); + MOZ_ASSERT(!aSeg1 || SVGPathSegUtils::DecodeType(*aSeg1) == segType, + "unexpected segment type"); + + // FIRST: Directly copy the arguments that don't make sense to add. + aResultSeg[0] = aSeg2[0]; // encoded segment type + + bool isArcType = SVGPathSegUtils::IsArcType(segType); + if (isArcType) { + // Copy boolean arc flags. + MOZ_ASSERT(!aSeg1 || !ArcFlagsDiffer(aSeg1, aSeg2), + "Expecting arc flags to match"); + aResultSeg[LARGE_ARC_FLAG_IDX] = aSeg2[LARGE_ARC_FLAG_IDX]; + aResultSeg[SWEEP_FLAG_IDX] = aSeg2[SWEEP_FLAG_IDX]; + } + + // SECOND: Add the arguments that are supposed to be added. + // (The 1's below are to account for segment type) + uint32_t numArgs = SVGPathSegUtils::ArgCountForType(segType); + for (uint32_t i = 1; i < 1 + numArgs; ++i) { + // Need to skip arc flags for arc-type segments. (already handled them) + if (!(isArcType && (i == LARGE_ARC_FLAG_IDX || i == SWEEP_FLAG_IDX))) { + aResultSeg[i] = (aSeg1 ? aCoeff1 * aSeg1[i] : 0.0) + aCoeff2 * aSeg2[i]; + } + } + + // FINALLY: Shift iterators forward. ("1+" is to include seg-type) + if (aSeg1) { + aSeg1 += 1 + numArgs; + } + aSeg2 += 1 + numArgs; + aResultSeg += 1 + numArgs; +} + +/** + * 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; +} + +static void +ConvertPathSegmentData(SVGPathDataAndInfo::const_iterator& aStart, + SVGPathDataAndInfo::const_iterator& aEnd, + SVGPathDataAndInfo::iterator& aResult, + SVGPathTraversalState& aState) +{ + uint32_t startType = SVGPathSegUtils::DecodeType(*aStart); + uint32_t endType = SVGPathSegUtils::DecodeType(*aEnd); + + uint32_t segmentLengthIncludingType = + 1 + SVGPathSegUtils::ArgCountForType(startType); + + SVGPathDataAndInfo::const_iterator pResultSegmentBegin = aResult; + + if (startType == endType) { + // No conversion need, just directly copy aStart. + aEnd += segmentLengthIncludingType; + while (segmentLengthIncludingType) { + *aResult++ = *aStart++; + --segmentLengthIncludingType; + } + SVGPathSegUtils::TraversePathSegment(pResultSegmentBegin, aState); + return; + } + + MOZ_ASSERT + (SVGPathSegUtils::SameTypeModuloRelativeness(startType, endType), + "Incompatible path segment types passed to ConvertPathSegmentData!"); + + RelativenessAdjustmentType adjustmentType = + SVGPathSegUtils::IsRelativeType(startType) ? eRelativeToAbsolute + : eAbsoluteToRelative; + + MOZ_ASSERT + (segmentLengthIncludingType == + 1 + SVGPathSegUtils::ArgCountForType(endType), + "Compatible path segment types for interpolation had different lengths!"); + + aResult[0] = aEnd[0]; + + switch (endType) { + case PATHSEG_LINETO_HORIZONTAL_ABS: + case PATHSEG_LINETO_HORIZONTAL_REL: + aResult[1] = aStart[1] + + (adjustmentType == eRelativeToAbsolute ? 1 : -1) * aState.pos.x; + break; + case PATHSEG_LINETO_VERTICAL_ABS: + case PATHSEG_LINETO_VERTICAL_REL: + aResult[1] = aStart[1] + + (adjustmentType == eRelativeToAbsolute ? 1 : -1) * aState.pos.y; + break; + case PATHSEG_ARC_ABS: + case PATHSEG_ARC_REL: + aResult[1] = aStart[1]; + aResult[2] = aStart[2]; + aResult[3] = aStart[3]; + aResult[4] = aStart[4]; + aResult[5] = aStart[5]; + aResult[6] = aStart[6]; + aResult[7] = aStart[7]; + AdjustSegmentForRelativeness(adjustmentType, aResult + 6, aState); + break; + case PATHSEG_CURVETO_CUBIC_ABS: + case PATHSEG_CURVETO_CUBIC_REL: + aResult[5] = aStart[5]; + aResult[6] = aStart[6]; + AdjustSegmentForRelativeness(adjustmentType, aResult + 5, aState); + MOZ_FALLTHROUGH; + case PATHSEG_CURVETO_QUADRATIC_ABS: + case PATHSEG_CURVETO_QUADRATIC_REL: + case PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: + case PATHSEG_CURVETO_CUBIC_SMOOTH_REL: + aResult[3] = aStart[3]; + aResult[4] = aStart[4]; + AdjustSegmentForRelativeness(adjustmentType, aResult + 3, aState); + MOZ_FALLTHROUGH; + case PATHSEG_MOVETO_ABS: + case PATHSEG_MOVETO_REL: + case PATHSEG_LINETO_ABS: + case PATHSEG_LINETO_REL: + case PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: + case PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: + aResult[1] = aStart[1]; + aResult[2] = aStart[2]; + AdjustSegmentForRelativeness(adjustmentType, aResult + 1, aState); + break; + } + + SVGPathSegUtils::TraversePathSegment(pResultSegmentBegin, aState); + aStart += segmentLengthIncludingType; + aEnd += segmentLengthIncludingType; + aResult += segmentLengthIncludingType; +} + +static void +ConvertAllPathSegmentData(SVGPathDataAndInfo::const_iterator aStart, + SVGPathDataAndInfo::const_iterator aStartDataEnd, + SVGPathDataAndInfo::const_iterator aEnd, + SVGPathDataAndInfo::const_iterator aEndDataEnd, + SVGPathDataAndInfo::iterator aResult) +{ + SVGPathTraversalState state; + state.mode = SVGPathTraversalState::eUpdateOnlyStartAndCurrentPos; + while (aStart < aStartDataEnd && aEnd < aEndDataEnd) { + ConvertPathSegmentData(aStart, aEnd, aResult, state); + } + MOZ_ASSERT(aStart == aStartDataEnd && aEnd == aEndDataEnd, + "Failed to convert all path segment data! (Corrupt?)"); +} + +nsresult +SVGPathSegListSMILType::Add(nsSMILValue& aDest, + const nsSMILValue& aValueToAdd, + uint32_t aCount) const +{ + NS_PRECONDITION(aDest.mType == this, "Unexpected SMIL type"); + NS_PRECONDITION(aValueToAdd.mType == this, "Incompatible SMIL type"); + + SVGPathDataAndInfo& dest = + *static_cast<SVGPathDataAndInfo*>(aDest.mU.mPtr); + const SVGPathDataAndInfo& valueToAdd = + *static_cast<const SVGPathDataAndInfo*>(aValueToAdd.mU.mPtr); + + if (valueToAdd.IsIdentity()) { // Adding identity value - no-op + return NS_OK; + } + + if (!dest.IsIdentity()) { + // Neither value is identity; make sure they're compatible. + MOZ_ASSERT(dest.Element() == valueToAdd.Element(), + "adding values from different elements...?"); + + PathInterpolationResult check = CanInterpolate(dest, valueToAdd); + if (check == eCannotInterpolate) { + // SVGContentUtils::ReportToConsole - can't add path segment lists with + // different numbers of segments, with arcs that have different flag + // values, or with incompatible segment types. + return NS_ERROR_FAILURE; + } + if (check == eRequiresConversion) { + // Convert dest, in-place, to match the types in valueToAdd: + ConvertAllPathSegmentData(dest.begin(), dest.end(), + valueToAdd.begin(), valueToAdd.end(), + dest.begin()); + } + } + + return AddWeightedPathSegLists(1.0, dest, aCount, valueToAdd, dest); +} + +nsresult +SVGPathSegListSMILType::ComputeDistance(const nsSMILValue& aFrom, + const nsSMILValue& aTo, + double& aDistance) const +{ + NS_PRECONDITION(aFrom.mType == this, "Unexpected SMIL type"); + NS_PRECONDITION(aTo.mType == this, "Incompatible SMIL type"); + + // See https://bugzilla.mozilla.org/show_bug.cgi?id=522306#c18 + + // SVGContentUtils::ReportToConsole + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsresult +SVGPathSegListSMILType::Interpolate(const nsSMILValue& aStartVal, + const nsSMILValue& aEndVal, + double aUnitDistance, + nsSMILValue& aResult) const +{ + NS_PRECONDITION(aStartVal.mType == aEndVal.mType, + "Trying to interpolate different types"); + NS_PRECONDITION(aStartVal.mType == this, + "Unexpected types for interpolation"); + NS_PRECONDITION(aResult.mType == this, "Unexpected result type"); + + const SVGPathDataAndInfo& start = + *static_cast<const SVGPathDataAndInfo*>(aStartVal.mU.mPtr); + const SVGPathDataAndInfo& end = + *static_cast<const SVGPathDataAndInfo*>(aEndVal.mU.mPtr); + SVGPathDataAndInfo& result = + *static_cast<SVGPathDataAndInfo*>(aResult.mU.mPtr); + MOZ_ASSERT(result.IsIdentity(), + "expecting outparam to start out as identity"); + + PathInterpolationResult check = CanInterpolate(start, end); + + if (check == eCannotInterpolate) { + // SVGContentUtils::ReportToConsole - can't interpolate path segment lists with + // different numbers of segments, with arcs with different flag values, or + // with incompatible segment types. + return NS_ERROR_FAILURE; + } + + const SVGPathDataAndInfo* startListToUse = &start; + if (check == eRequiresConversion) { + // Can't convert |start| in-place, since it's const. Instead, we copy it + // into |result|, converting the types as we go, and use that as our start. + if (!result.SetLength(end.Length())) { + return NS_ERROR_OUT_OF_MEMORY; + } + result.SetElement(end.Element()); // propagate target element info! + + ConvertAllPathSegmentData(start.begin(), start.end(), + end.begin(), end.end(), + result.begin()); + startListToUse = &result; + } + + return AddWeightedPathSegLists(1.0 - aUnitDistance, *startListToUse, + aUnitDistance, end, result); +} + +} // namespace mozilla |