mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-07-11 22:29:44 +00:00
502 lines
18 KiB
C++
502 lines
18 KiB
C++
/* -*- 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);
|
|
// fall through
|
|
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);
|
|
// fall through
|
|
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
|