diff --git a/mozilla/content/svg/content/src/nsSVGPathDataParser.cpp b/mozilla/content/svg/content/src/nsSVGPathDataParser.cpp index e35f3748859..421c59de93c 100644 --- a/mozilla/content/svg/content/src/nsSVGPathDataParser.cpp +++ b/mozilla/content/svg/content/src/nsSVGPathDataParser.cpp @@ -1075,104 +1075,27 @@ nsSVGPathDataParserToInternal::ConvertArcToCurves(float x2, float y2, PRBool largeArcFlag, PRBool sweepFlag) { - const double radPerDeg = M_PI/180.0; - - double x1=mPx, y1=mPy; - - // 1. Treat out-of-range parameters as described in + float x1=mPx, y1=mPy, x3, y3; + // Treat out-of-range parameters as described in // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes // If the endpoints (x1, y1) and (x2, y2) are identical, then this // is equivalent to omitting the elliptical arc segment entirely - if (x1 == x2 && y1 == y2) + if (x1 == x2 && y1 == y2) { return NS_OK; - + } // If rX = 0 or rY = 0 then this arc is treated as a straight line // segment (a "lineto") joining the endpoints. if (rx == 0.0f || ry == 0.0f) { return PathLineTo(x2, y2); } - - // If rX or rY have negative signs, these are dropped; the absolute - // value is used instead. - if (rx<0.0) rx = -rx; - if (ry<0.0) ry = -ry; + nsSVGArcConverter converter(x1, y1, x2, y2, rx, ry, angle, + largeArcFlag, sweepFlag); - // 2. convert to center parameterization as shown in - // http://www.w3.org/TR/SVG/implnote.html - double sinPhi = sin(angle*radPerDeg); - double cosPhi = cos(angle*radPerDeg); - - double x1dash = cosPhi * (x1-x2)/2.0 + sinPhi * (y1-y2)/2.0; - double y1dash = -sinPhi * (x1-x2)/2.0 + cosPhi * (y1-y2)/2.0; - - double root; - double numerator = rx*rx*ry*ry - rx*rx*y1dash*y1dash - ry*ry*x1dash*x1dash; - - if (numerator < 0.0) { - // If rX , rY and are such that there is no solution (basically, - // the ellipse is not big enough to reach from (x1, y1) to (x2, - // y2)) then the ellipse is scaled up uniformly until there is - // exactly one solution (until the ellipse is just big enough). - - // -> find factor s, such that numerator' with rx'=s*rx and - // ry'=s*ry becomes 0 : - float s = (float)sqrt(1.0 - numerator/(rx*rx*ry*ry)); - - rx *= s; - ry *= s; - root = 0.0; - - } - else { - root = (largeArcFlag == sweepFlag ? -1.0 : 1.0) * - sqrt( numerator/(rx*rx*y1dash*y1dash + ry*ry*x1dash*x1dash) ); - } - - double cxdash = root*rx*y1dash/ry; - double cydash = -root*ry*x1dash/rx; - - double cx = cosPhi * cxdash - sinPhi * cydash + (x1+x2)/2.0; - double cy = sinPhi * cxdash + cosPhi * cydash + (y1+y2)/2.0; - double theta1 = CalcVectorAngle(1.0, 0.0, (x1dash-cxdash)/rx, (y1dash-cydash)/ry); - double dtheta = CalcVectorAngle((x1dash-cxdash)/rx, (y1dash-cydash)/ry, - (-x1dash-cxdash)/rx, (-y1dash-cydash)/ry); - if (!sweepFlag && dtheta>0) - dtheta -= 2.0*M_PI; - else if (sweepFlag && dtheta<0) - dtheta += 2.0*M_PI; - - // 3. convert into cubic bezier segments <= 90deg - int segments = (int)ceil(fabs(dtheta/(M_PI/2.0))); - double delta = dtheta/segments; - double t = 8.0/3.0 * sin(delta/4.0) * sin(delta/4.0) / sin(delta/2.0); - - for (int i = 0; i < segments; ++i) { - double cosTheta1 = cos(theta1); - double sinTheta1 = sin(theta1); - double theta2 = theta1 + delta; - double cosTheta2 = cos(theta2); - double sinTheta2 = sin(theta2); - - // a) calculate endpoint of the segment: - double xe = cosPhi * rx*cosTheta2 - sinPhi * ry*sinTheta2 + cx; - double ye = sinPhi * rx*cosTheta2 + cosPhi * ry*sinTheta2 + cy; - - // b) calculate gradients at start/end points of segment: - double dx1 = t * ( - cosPhi * rx*sinTheta1 - sinPhi * ry*cosTheta1); - double dy1 = t * ( - sinPhi * rx*sinTheta1 + cosPhi * ry*cosTheta1); - - double dxe = t * ( cosPhi * rx*sinTheta2 + sinPhi * ry*cosTheta2); - double dye = t * ( sinPhi * rx*sinTheta2 - cosPhi * ry*cosTheta2); - + while (converter.GetNextSegment(&x1, &y1, &x2, &y2, &x3, &y3)) { // c) draw the cubic bezier: - nsresult rv = PathCurveTo(x1+dx1, y1+dy1, xe+dxe, ye+dye, xe, ye); + nsresult rv = PathCurveTo(x1, y1, x2, y2, x3, y3); NS_ENSURE_SUCCESS(rv, rv); - - // do next segment - theta1 = theta2; - x1 = (float)xe; - y1 = (float)ye; } return NS_OK; @@ -1420,3 +1343,107 @@ nsSVGPathDataParserToDOM::StoreEllipticalArc(PRBool absCoords, largeArcFlag, sweepFlag)); } +nsSVGArcConverter::nsSVGArcConverter(float x1, float y1, + float x2, float y2, + float rx, float ry, + float angle, + PRBool largeArcFlag, + PRBool sweepFlag) +{ + const double radPerDeg = M_PI/180.0; + + // If rX or rY have negative signs, these are dropped; the absolute + // value is used instead. + mRx = fabs(rx); + mRy = fabs(ry); + + // Convert to center parameterization as shown in + // http://www.w3.org/TR/SVG/implnote.html + mSinPhi = sin(angle*radPerDeg); + mCosPhi = cos(angle*radPerDeg); + + double x1dash = mCosPhi * (x1-x2)/2.0 + mSinPhi * (y1-y2)/2.0; + double y1dash = -mSinPhi * (x1-x2)/2.0 + mCosPhi * (y1-y2)/2.0; + + double root; + double numerator = mRx*mRx*mRy*mRy - mRx*mRx*y1dash*y1dash - + mRy*mRy*x1dash*x1dash; + + if (numerator < 0.0) { + // If mRx , mRy and are such that there is no solution (basically, + // the ellipse is not big enough to reach from (x1, y1) to (x2, + // y2)) then the ellipse is scaled up uniformly until there is + // exactly one solution (until the ellipse is just big enough). + + // -> find factor s, such that numerator' with mRx'=s*mRx and + // mRy'=s*mRy becomes 0 : + float s = (float)sqrt(1.0 - numerator/(mRx*mRx*mRy*mRy)); + + mRx *= s; + mRy *= s; + root = 0.0; + + } + else { + root = (largeArcFlag == sweepFlag ? -1.0 : 1.0) * + sqrt( numerator/(mRx*mRx*y1dash*y1dash + mRy*mRy*x1dash*x1dash) ); + } + + double cxdash = root*mRx*y1dash/mRy; + double cydash = -root*mRy*x1dash/mRx; + + mCx = mCosPhi * cxdash - mSinPhi * cydash + (x1+x2)/2.0; + mCy = mSinPhi * cxdash + mCosPhi * cydash + (y1+y2)/2.0; + mTheta = CalcVectorAngle(1.0, 0.0, (x1dash-cxdash)/mRx, (y1dash-cydash)/mRy); + double dtheta = CalcVectorAngle((x1dash-cxdash)/mRx, (y1dash-cydash)/mRy, + (-x1dash-cxdash)/mRx, (-y1dash-cydash)/mRy); + if (!sweepFlag && dtheta>0) + dtheta -= 2.0*M_PI; + else if (sweepFlag && dtheta<0) + dtheta += 2.0*M_PI; + + // Convert into cubic bezier segments <= 90deg + mNumSegs = NS_STATIC_CAST(int, ceil(fabs(dtheta/(M_PI/2.0)))); + mDelta = dtheta/mNumSegs; + mT = 8.0/3.0 * sin(mDelta/4.0) * sin(mDelta/4.0) / sin(mDelta/2.0); + + mX1 = x1; + mY1 = y1; + mSegIndex = 0; +} + +PRBool +nsSVGArcConverter::GetNextSegment(float *x1, float *y1, + float *x2, float *y2, + float *x3, float *y3) +{ + if (mSegIndex == mNumSegs) { + return PR_FALSE; + } + + float cosTheta1 = cos(mTheta); + float sinTheta1 = sin(mTheta); + float theta2 = mTheta + mDelta; + float cosTheta2 = cos(theta2); + float sinTheta2 = sin(theta2); + + // a) calculate endpoint of the segment: + *x3 = mCosPhi * mRx*cosTheta2 - mSinPhi * mRy*sinTheta2 + mCx; + *y3 = mSinPhi * mRx*cosTheta2 + mCosPhi * mRy*sinTheta2 + mCy; + + // b) calculate gradients at start/end points of segment: + *x1 = mX1 + mT * ( - mCosPhi * mRx*sinTheta1 - mSinPhi * mRy*cosTheta1); + *y1 = mY1 + mT * ( - mSinPhi * mRx*sinTheta1 + mCosPhi * mRy*cosTheta1); + + *x2 = *x3 + mT * ( mCosPhi * mRx*sinTheta2 + mSinPhi * mRy*cosTheta2); + *y2 = *y3 + mT * ( mSinPhi * mRx*sinTheta2 - mCosPhi * mRy*cosTheta2); + + // do next segment + mTheta = theta2; + mX1 = *x3; + mY1 = *y3; + ++mSegIndex; + + return PR_TRUE; +} + diff --git a/mozilla/content/svg/content/src/nsSVGPathDataParser.h b/mozilla/content/svg/content/src/nsSVGPathDataParser.h index 025de57dc40..c7021da780e 100644 --- a/mozilla/content/svg/content/src/nsSVGPathDataParser.h +++ b/mozilla/content/svg/content/src/nsSVGPathDataParser.h @@ -221,4 +221,24 @@ private: nsCOMArray* mData; }; +class nsSVGArcConverter +{ +public: + nsSVGArcConverter(float x1, float y1, + float x2, float y2, + float rx, float ry, + float angle, + PRBool largeArcFlag, + PRBool sweepFlag); + PRBool GetNextSegment(float *x1, float *y1, + float *x2, float *y2, + float *x3, float *y3); +protected: + PRInt32 mNumSegs, mSegIndex; + float mTheta, mDelta, mT; + float mSinPhi, mCosPhi; + float mX1, mY1, mRx, mRy, mCx, mCy; + +}; + #endif // __NS_SVGPATHDATAPARSER_H__ diff --git a/mozilla/content/svg/content/src/nsSVGPathElement.cpp b/mozilla/content/svg/content/src/nsSVGPathElement.cpp index 152289bbe46..94b6b46765d 100644 --- a/mozilla/content/svg/content/src/nsSVGPathElement.cpp +++ b/mozilla/content/svg/content/src/nsSVGPathElement.cpp @@ -139,7 +139,38 @@ nsSVGPathElement::GetPointAtLength(float distance, nsIDOMSVGPoint **_retval) NS_IMETHODIMP nsSVGPathElement::GetPathSegAtLength(float distance, PRUint32 *_retval) { - return NS_ERROR_NOT_IMPLEMENTED; + //Check if mSegments is null + nsresult rv = CreatePathSegList(); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 i = 0, numSegments; + float distCovered = 0; + nsSVGPathSegTraversalState ts; + + mSegments->GetNumberOfItems(&numSegments); + + // There is no need to check to see if distance falls within the last segment + // because if distance is longer than the total length of the path we return + // the index of the final segment anyway. + while (distCovered < distance && i < numSegments - 1) { + nsIDOMSVGPathSeg *iSeg; + mSegments->GetItem(i, &iSeg); + nsSVGPathSeg* curSeg = NS_STATIC_CAST(nsSVGPathSeg*, iSeg); + if (i == 0) { + curSeg->GetLength(&ts); + } else { + distCovered += curSeg->GetLength(&ts); + } + + if (distCovered >= distance) { + break; + } + ++i; + } + + *_retval = i; + + return NS_OK; } /* nsIDOMSVGPathSegClosePath createSVGPathSegClosePath (); */ diff --git a/mozilla/content/svg/content/src/nsSVGPathSeg.cpp b/mozilla/content/svg/content/src/nsSVGPathSeg.cpp index d78043cbf3a..fff8254032b 100644 --- a/mozilla/content/svg/content/src/nsSVGPathSeg.cpp +++ b/mozilla/content/svg/content/src/nsSVGPathSeg.cpp @@ -42,6 +42,15 @@ #include "nsTextFormatter.h" #include "nsContentUtils.h" + +struct PathPoint { + float x, y; +}; + +//---------------------------------------------------------------------- +// Error threshold to use when calculating the length of Bezier curves +static const float PATH_SEG_LENGTH_TOLERANCE = 0.0000001f; + //---------------------------------------------------------------------- // implementation helper macros @@ -54,6 +63,73 @@ NS_INTERFACE_MAP_BEGIN(ns##basename) \ NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(basename) \ NS_INTERFACE_MAP_END_INHERITING(nsSVGPathSeg) +//---------------------------------------------------------------------- +// GetLength Helper Functions + +static float GetDistance(float x1, float x2, float y1, float y2) +{ + return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); +} + +static void SplitQuadraticBezier(PathPoint *curve, + PathPoint *left, + PathPoint *right) +{ + left[0].x = curve[0].x; + left[0].y = curve[0].y; + right[2].x = curve[2].x; + right[2].y = curve[2].y; + left[1].x = (curve[0].x + curve[1].x) / 2; + left[1].y = (curve[0].y + curve[1].y) / 2; + right[1].x = (curve[1].x + curve[2].x) / 2; + right[1].y = (curve[1].y + curve[2].y) / 2; + left[2].x = right[0].x = (left[1].x + right[1].x) / 2; + left[2].y = right[0].y = (left[1].y + right[1].y) / 2; +} + +static void SplitCubicBezier(PathPoint *curve, + PathPoint *left, + PathPoint *right) +{ + PathPoint tmp; + tmp.x = (curve[1].x + curve[2].x) / 4; + tmp.y = (curve[1].y + curve[2].y) / 4; + left[0].x = curve[0].x; + left[0].y = curve[0].y; + right[3].x = curve[3].x; + right[3].y = curve[3].y; + left[1].x = (curve[0].x + curve[1].x) / 2; + left[1].y = (curve[0].y + curve[1].y) / 2; + right[2].x = (curve[2].x + curve[3].x) / 2; + right[2].y = (curve[2].y + curve[3].y) / 2; + left[2].x = left[1].x / 2 + tmp.x; + left[2].y = left[1].y / 2 + tmp.y; + right[1].x = right[2].x / 2 + tmp.x; + right[1].y = right[2].y / 2 + tmp.y; + left[3].x = right[0].x = (left[2].x + right[1].x) / 2; + left[3].y = right[0].y = (left[2].y + right[1].y) / 2; +} + +static float CalcBezLength(PathPoint *curve,PRUint32 numPts, + void (*split)(PathPoint*, PathPoint*, PathPoint*)) +{ + PathPoint left[4]; + PathPoint right[4]; + float length = 0, dist; + PRUint32 i; + for (i = 0; i < numPts - 1; i++) { + length += GetDistance(curve[i].x, curve[i+1].x, curve[i].y, curve[i+1].y); + } + dist = GetDistance(curve[0].x, curve[numPts - 1].x, + curve[0].y, curve[numPts - 1].y); + if (length - dist > PATH_SEG_LENGTH_TOLERANCE) { + split(curve, left, right); + return CalcBezLength(left, numPts, split) + + CalcBezLength(right, numPts, split); + } + return length; +} + //////////////////////////////////////////////////////////////////////// // nsSVGPathSeg @@ -127,7 +203,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH; } }; @@ -154,6 +230,16 @@ nsSVGPathSegClosePath::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegClosePath::GetLength(nsSVGPathSegTraversalState *ts) +{ + float dist = GetDistance(ts->startPosX, ts->curPosX, + ts->startPosY, ts->curPosY); + ts->quadCPX = ts->cubicCPX = ts->curPosX = ts->startPosX; + ts->quadCPY = ts->cubicCPY = ts->curPosY = ts->startPosY; + return dist; +} + //////////////////////////////////////////////////////////////////////// // nsSVGPathSegMovetoAbs @@ -171,7 +257,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS; } @@ -210,6 +296,14 @@ nsSVGPathSegMovetoAbs::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegMovetoAbs::GetLength(nsSVGPathSegTraversalState *ts) +{ + ts->cubicCPX = ts->quadCPX = ts->startPosX = ts->curPosX = mX; + ts->cubicCPY = ts->quadCPY = ts->startPosY = ts->curPosY = mY; + return 0; +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegMovetoAbs methods: @@ -257,7 +351,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL; } @@ -295,6 +389,15 @@ nsSVGPathSegMovetoRel::GetValueString(nsAString& aValue) return NS_OK; } + +float +nsSVGPathSegMovetoRel::GetLength(nsSVGPathSegTraversalState *ts) +{ + ts->cubicCPX = ts->quadCPX = ts->startPosX = ts->curPosX += mX; + ts->cubicCPY = ts->quadCPY = ts->startPosY = ts->curPosY += mY; + return 0; +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegMovetoRel methods: @@ -341,7 +444,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS; } @@ -379,6 +482,17 @@ nsSVGPathSegLinetoAbs::GetValueString(nsAString& aValue) return NS_OK; } + +float +nsSVGPathSegLinetoAbs::GetLength(nsSVGPathSegTraversalState *ts) +{ + + float dist = GetDistance(ts->curPosX, mX, ts->curPosY, mY); + ts->cubicCPX = ts->quadCPX = ts->curPosX = mX; + ts->cubicCPY = ts->quadCPY = ts->curPosY = mY; + return dist; +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegLinetoAbs methods: @@ -426,7 +540,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_LINETO_REL; } @@ -465,6 +579,14 @@ nsSVGPathSegLinetoRel::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegLinetoRel::GetLength(nsSVGPathSegTraversalState *ts) +{ + ts->cubicCPX = ts->quadCPX = ts->curPosX += mX; + ts->cubicCPY = ts->quadCPY = ts->curPosY += mY; + return sqrt(mX * mX + mY * mY); +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegLinetoRel methods: @@ -514,7 +636,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS; } @@ -558,6 +680,22 @@ nsSVGPathSegCurvetoCubicAbs::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegCurvetoCubicAbs::GetLength(nsSVGPathSegTraversalState *ts) +{ + PathPoint curve[4] = { {ts->curPosX, ts->curPosY}, + {mX1, mY1}, + {mX2, mY2}, + {mX, mY} }; + float dist = CalcBezLength(curve, 4, SplitCubicBezier); + + ts->quadCPX = ts->curPosX = mX; + ts->quadCPY = ts->curPosY = mY; + ts->cubicCPX = mX2; + ts->cubicCPY = mY2; + return dist; +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegCurvetoCubicAbs methods: @@ -659,7 +797,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL; } @@ -703,6 +841,19 @@ nsSVGPathSegCurvetoCubicRel::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegCurvetoCubicRel::GetLength(nsSVGPathSegTraversalState *ts) +{ + PathPoint curve[4] = { {0, 0}, {mX1, mY1}, {mX2, mY2}, {mX, mY} }; + float dist = CalcBezLength(curve, 4, SplitCubicBezier); + + ts->cubicCPX = mX2 + ts->curPosX; + ts->cubicCPY = mY2 + ts->curPosY; + ts->quadCPX = ts->curPosX += mX; + ts->quadCPY = ts->curPosY += mY; + return dist; +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegCurvetoCubicRel methods: @@ -802,7 +953,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS; } @@ -845,6 +996,19 @@ nsSVGPathSegCurvetoQuadraticAbs::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegCurvetoQuadraticAbs::GetLength(nsSVGPathSegTraversalState *ts) +{ + PathPoint curve[3] = { {ts->curPosX, ts->curPosY}, {mX1, mY1}, {mX, mY} }; + float dist = CalcBezLength(curve, 3, SplitQuadraticBezier); + + ts->quadCPX = mX1; + ts->quadCPY = mY1; + ts->cubicCPX = ts->curPosX = mX; + ts->cubicCPY = ts->curPosY = mY; + return dist; +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegCurvetoQuadraticAbs methods: @@ -919,7 +1083,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL; } @@ -963,6 +1127,19 @@ nsSVGPathSegCurvetoQuadraticRel::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegCurvetoQuadraticRel::GetLength(nsSVGPathSegTraversalState *ts) +{ + PathPoint curve[3] = { {0, 0}, {mX1, mY1}, {mX, mY} }; + float dist = CalcBezLength(curve, 3, SplitQuadraticBezier); + + ts->quadCPX = mX1 + ts->curPosX; + ts->quadCPY = mY1 + ts->curPosY; + ts->cubicCPX = ts->curPosX += mX; + ts->cubicCPY = ts->curPosY += mY; + return dist; +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegCurvetoQuadraticRel methods: @@ -1038,7 +1215,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_ARC_ABS; } @@ -1086,6 +1263,25 @@ nsSVGPathSegArcAbs::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegArcAbs::GetLength(nsSVGPathSegTraversalState *ts) +{ + PathPoint bez[4] = { {ts->curPosX, ts->curPosY}, {0,0}, {0,0}, {0,0}}; + nsSVGArcConverter converter(ts->curPosX, ts->curPosY, mX, mY, mR1, + mR2, mAngle, mLargeArcFlag, mSweepFlag); + float dist = 0; + while (converter.GetNextSegment(&bez[1].x, &bez[1].y, &bez[2].x, &bez[2].y, + &bez[3].x, &bez[3].y)) + { + dist += CalcBezLength(bez, 4, SplitCubicBezier); + bez[0].x = bez[3].x; + bez[0].y = bez[3].y; + } + ts->cubicCPX = ts->quadCPX = ts->curPosX = mX; + ts->cubicCPY = ts->quadCPY = ts->curPosY = mY; + return dist; +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegArcAbs methods: @@ -1200,7 +1396,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_ARC_REL; } @@ -1247,6 +1443,26 @@ nsSVGPathSegArcRel::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegArcRel::GetLength(nsSVGPathSegTraversalState *ts) +{ + PathPoint bez[4] = { {0, 0}, {0,0}, {0,0}, {0,0}}; + nsSVGArcConverter converter(0, 0, mX, mY, mR1, mR2, mAngle, + mLargeArcFlag, mSweepFlag); + float dist = 0; + while (converter.GetNextSegment(&bez[1].x, &bez[1].y, &bez[2].x, &bez[2].y, + &bez[3].x, &bez[3].y)) + { + dist += CalcBezLength(bez, 4, SplitCubicBezier); + bez[0].x = bez[3].x; + bez[0].y = bez[3].y; + } + + ts->cubicCPX = ts->quadCPX = ts->curPosX += mX; + ts->cubicCPY = ts->quadCPY = ts->curPosY += mY; + return dist; +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegArcRel methods: @@ -1359,7 +1575,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS; } @@ -1399,6 +1615,15 @@ nsSVGPathSegLinetoHorizontalAbs::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegLinetoHorizontalAbs::GetLength(nsSVGPathSegTraversalState *ts) +{ + float dist = fabs(mX - ts->curPosX); + ts->cubicCPX = ts->quadCPX = ts->curPosX = mX; + ts->cubicCPY = ts->quadCPY = ts->curPosY; + return dist; +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegLinetoHorizontalAbs methods: @@ -1432,7 +1657,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL; } @@ -1472,6 +1697,14 @@ nsSVGPathSegLinetoHorizontalRel::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegLinetoHorizontalRel::GetLength(nsSVGPathSegTraversalState *ts) +{ + ts->cubicCPX = ts->quadCPX = ts->curPosX += mX; + ts->cubicCPY = ts->quadCPY = ts->curPosY; + return fabs(mX); +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegLinetoHorizontalRel methods: @@ -1505,7 +1738,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS; } @@ -1545,6 +1778,15 @@ nsSVGPathSegLinetoVerticalAbs::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegLinetoVerticalAbs::GetLength(nsSVGPathSegTraversalState *ts) +{ + float dist = fabs(mY - ts->curPosY); + ts->cubicCPY = ts->quadCPY = ts->curPosY = mY; + ts->cubicCPX = ts->quadCPX = ts->curPosX; + return dist; +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegLinetoVerticalAbs methods: @@ -1578,7 +1820,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL; } @@ -1619,6 +1861,14 @@ nsSVGPathSegLinetoVerticalRel::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegLinetoVerticalRel::GetLength(nsSVGPathSegTraversalState *ts) +{ + ts->cubicCPY = ts->quadCPY = ts->curPosY += mY; + ts->cubicCPX = ts->quadCPX = ts->curPosX; + return fabs(mY); +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegLinetoVerticalRel methods: @@ -1653,7 +1903,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS; } @@ -1696,6 +1946,25 @@ nsSVGPathSegCurvetoCubicSmoothAbs::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegCurvetoCubicSmoothAbs::GetLength(nsSVGPathSegTraversalState *ts) +{ + float x1 = 2 * ts->curPosX - ts->cubicCPX; + float y1 = 2 * ts->curPosY - ts->cubicCPY; + + PathPoint curve[4] = { {ts->curPosX, ts->curPosY}, + {x1, y1}, + {mX2, mY2}, + {mX, mY} }; + float dist = CalcBezLength(curve, 4, SplitCubicBezier); + + ts->cubicCPX = mX2; + ts->cubicCPY = mY2; + ts->quadCPX = ts->curPosX = mX; + ts->quadCPY = ts->curPosY = mY; + return dist; +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegCurvetoCubicSmoothAbs methods: @@ -1769,7 +2038,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL; } @@ -1812,6 +2081,23 @@ nsSVGPathSegCurvetoCubicSmoothRel::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegCurvetoCubicSmoothRel::GetLength(nsSVGPathSegTraversalState *ts) +{ + + float x1 = ts->curPosX - ts->cubicCPX; + float y1 = ts->curPosY - ts->cubicCPY; + + PathPoint curve[4] = { {0, 0}, {x1, y1}, {mX2, mY2}, {mX, mY} }; + float dist = CalcBezLength(curve, 4, SplitCubicBezier); + + ts->cubicCPX = mX2 + ts->curPosX; + ts->cubicCPY = mY2 + ts->curPosY; + ts->quadCPX = ts->curPosX += mX; + ts->quadCPY = ts->curPosY += mY; + return dist; +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegCurvetoCubicSmoothRel methods: @@ -1884,7 +2170,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS; } @@ -1923,6 +2209,22 @@ nsSVGPathSegCurvetoQuadraticSmoothAbs::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegCurvetoQuadraticSmoothAbs::GetLength(nsSVGPathSegTraversalState *ts) +{ + ts->quadCPX = 2 * ts->curPosX - ts->quadCPX; + ts->quadCPY = 2 * ts->curPosY - ts->quadCPY; + + PathPoint bez[3] = { {ts->curPosX, ts->curPosY}, + {ts->quadCPX, ts->quadCPY}, + {mX, mY} }; + float dist = CalcBezLength(bez, 3, SplitQuadraticBezier); + + ts->cubicCPX = ts->curPosX = mX; + ts->cubicCPY = ts->curPosY = mY; + return dist; +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegCurvetoQuadraticSmoothAbs methods: @@ -1969,7 +2271,7 @@ public: // nsSVGPathSeg methods: NS_IMETHOD GetValueString(nsAString& aValue); - + virtual float GetLength(nsSVGPathSegTraversalState *ts); virtual PRUint16 GetSegType() { return nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL; } @@ -2010,6 +2312,23 @@ nsSVGPathSegCurvetoQuadraticSmoothRel::GetValueString(nsAString& aValue) return NS_OK; } +float +nsSVGPathSegCurvetoQuadraticSmoothRel::GetLength(nsSVGPathSegTraversalState *ts) +{ + ts->quadCPX = ts->curPosX - ts->quadCPX; + ts->quadCPY = ts->curPosY - ts->quadCPY; + + PathPoint bez[3] = { {0, 0}, {ts->quadCPX, ts->quadCPY}, {mX, mY} }; + float dist = CalcBezLength(bez, 3, SplitQuadraticBezier); + + ts->quadCPX += ts->curPosX; + ts->quadCPY += ts->curPosY; + + ts->cubicCPX = ts->curPosX += mX; + ts->cubicCPY = ts->curPosY += mY; + return dist; +} + //---------------------------------------------------------------------- // nsIDOMSVGPathSegCurvetoQuadraticSmoothRel methods: diff --git a/mozilla/content/svg/content/src/nsSVGPathSeg.h b/mozilla/content/svg/content/src/nsSVGPathSeg.h index 200a126e1b4..132555945a1 100644 --- a/mozilla/content/svg/content/src/nsSVGPathSeg.h +++ b/mozilla/content/svg/content/src/nsSVGPathSeg.h @@ -41,10 +41,22 @@ #include "nsIDOMSVGPathSeg.h" #include "nsIWeakReference.h" +#include "nsSVGUtils.h" +#include "nsSVGPathDataParser.h" class nsSVGPathSegList; class nsISVGValue; +struct nsSVGPathSegTraversalState { + float curPosX, startPosX, quadCPX, cubicCPX; + float curPosY, startPosY, quadCPY, cubicCPY; + nsSVGPathSegTraversalState() + { + curPosX = startPosX = quadCPX = cubicCPX = 0; + curPosY = startPosY = quadCPY = cubicCPY = 0; + } +}; + class nsSVGPathSeg : public nsIDOMSVGPathSeg { public: @@ -58,6 +70,9 @@ public: NS_DECL_NSIDOMSVGPATHSEG NS_IMETHOD GetValueString(nsAString& aValue) = 0; + // nsSVGPathSeg methods: + virtual float GetLength(nsSVGPathSegTraversalState *ts) = 0; + protected: virtual PRUint16 GetSegType() = 0; void DidModify();