Bug 334999 - Compact path storage. r+sr=roc

This commit is contained in:
tor%cs.brown.edu 2006-05-03 17:01:28 +00:00
parent c659dbc2d9
commit 74c52378a7
8 changed files with 1067 additions and 723 deletions

View File

@ -167,6 +167,8 @@ EXPORTS = \
include $(topsrcdir)/config/rules.mk
CFLAGS += $(MOZ_CAIRO_CFLAGS)
CXXFLAGS += $(MOZ_CAIRO_CFLAGS)
INCLUDES += \
-I$(srcdir)/../../../shared/public \

View File

@ -41,6 +41,8 @@
#include <stdlib.h>
#include <math.h>
class nsIFrame;
////////////////////////////////////////////////////////////////////////
// nsISVGPathFlatten

View File

@ -38,7 +38,11 @@
#include "nsSVGPathDataParser.h"
#include "nsSVGPathSeg.h"
#include "nsSVGPathElement.h"
#include "prdtoa.h"
#include "nsSVGUtils.h"
#include <stdlib.h>
#include <math.h>
//----------------------------------------------------------------------
// helper macros
@ -48,32 +52,28 @@
//----------------------------------------------------------------------
// public interface
nsSVGPathDataParser::nsSVGPathDataParser(nsVoidArray* data)
: mData(data)
{
}
nsresult nsSVGPathDataParser::Parse(const char* str)
nsresult
nsSVGPathDataParser::Parse(const nsAString &aValue)
{
nsresult rv = NS_OK;
char *str = ToNewCString(aValue);
if (!str)
return NS_ERROR_OUT_OF_MEMORY;
inputpos = str;
getNextToken();
rv = matchSvgPath();
if (tokentype != END)
rv = NS_ERROR_FAILURE; // not all tokens were consumed
nsMemory::Free(str);
return rv;
}
//----------------------------------------------------------------------
// helpers
nsresult nsSVGPathDataParser::AppendSegment(nsIDOMSVGPathSeg* seg)
{
NS_ADDREF(seg);
mData->AppendElement((void*)seg);
return NS_OK;
}
void nsSVGPathDataParser::getNextToken()
{
@ -314,21 +314,8 @@ nsresult nsSVGPathDataParser::matchMovetoArgSeq(PRBool absCoords)
float x, y;
ENSURE_MATCHED(matchCoordPair(&x, &y));
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegMovetoAbs> segAbs;
rv = NS_NewSVGPathSegMovetoAbs(getter_AddRefs(segAbs), x, y);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegMovetoRel> segRel;
rv = NS_NewSVGPathSegMovetoRel(getter_AddRefs(segRel), x, y);
seg = segRel;
}
if (NS_FAILED(rv)) return rv;
rv = AppendSegment(seg);
if (NS_FAILED(rv)) return rv;
nsresult rv = StoreMoveTo(absCoords, x, y);
NS_ENSURE_SUCCESS(rv, rv);
const char* pos = tokenpos;
@ -359,14 +346,7 @@ nsresult nsSVGPathDataParser::matchClosePath()
return NS_ERROR_FAILURE;
}
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSegClosePath> seg;
rv = NS_NewSVGPathSegClosePath(getter_AddRefs(seg));
if (NS_FAILED(rv)) return rv;
rv = AppendSegment(seg);
if (NS_FAILED(rv)) return rv;
return NS_OK;
return StoreClosePath();
}
//----------------------------------------------------------------------
@ -403,22 +383,9 @@ nsresult nsSVGPathDataParser::matchLinetoArgSeq(PRBool absCoords)
float x, y;
ENSURE_MATCHED(matchCoordPair(&x, &y));
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegLinetoAbs> segAbs;
rv = NS_NewSVGPathSegLinetoAbs(getter_AddRefs(segAbs), x, y);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegLinetoRel> segRel;
rv = NS_NewSVGPathSegLinetoRel(getter_AddRefs(segRel), x, y);
seg = segRel;
}
if (NS_FAILED(rv)) return rv;
rv = AppendSegment(seg);
if (NS_FAILED(rv)) return rv;
nsresult rv = StoreLineTo(absCoords, x, y);
NS_ENSURE_SUCCESS(rv, rv);
const char* pos = tokenpos;
if (isTokenCommaWspStarter()) {
@ -473,22 +440,9 @@ nsresult nsSVGPathDataParser::matchHorizontalLinetoArgSeq(PRBool absCoords)
float x;
ENSURE_MATCHED(matchCoord(&x));
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalAbs> segAbs;
rv = NS_NewSVGPathSegLinetoHorizontalAbs(getter_AddRefs(segAbs), x);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalRel> segRel;
rv = NS_NewSVGPathSegLinetoHorizontalRel(getter_AddRefs(segRel), x);
seg = segRel;
}
if (NS_FAILED(rv)) return rv;
rv = AppendSegment(seg);
if (NS_FAILED(rv)) return rv;
nsresult rv = StoreHLineTo(absCoords, x);
NS_ENSURE_SUCCESS(rv, rv);
const char* pos = tokenpos;
if (isTokenCommaWspStarter()) {
@ -538,22 +492,9 @@ nsresult nsSVGPathDataParser::matchVerticalLinetoArgSeq(PRBool absCoords)
float y;
ENSURE_MATCHED(matchCoord(&y));
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalAbs> segAbs;
rv = NS_NewSVGPathSegLinetoVerticalAbs(getter_AddRefs(segAbs), y);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalRel> segRel;
rv = NS_NewSVGPathSegLinetoVerticalRel(getter_AddRefs(segRel), y);
seg = segRel;
}
if (NS_FAILED(rv)) return rv;
rv = AppendSegment(seg);
if (NS_FAILED(rv)) return rv;
nsresult rv = StoreVLineTo(absCoords, y);
NS_ENSURE_SUCCESS(rv, rv);
const char* pos = tokenpos;
if (isTokenCommaWspStarter()) {
@ -603,22 +544,9 @@ nsresult nsSVGPathDataParser::matchCurvetoArgSeq(PRBool absCoords)
while(1) {
float x, y, x1, y1, x2, y2;
ENSURE_MATCHED(matchCurvetoArg(&x, &y, &x1, &y1, &x2, &y2));
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicAbs> segAbs;
rv = NS_NewSVGPathSegCurvetoCubicAbs(getter_AddRefs(segAbs), x, y, x1, y1, x2, y2);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicRel> segRel;
rv = NS_NewSVGPathSegCurvetoCubicRel(getter_AddRefs(segRel), x, y, x1, y1, x2, y2);
seg = segRel;
}
if (NS_FAILED(rv)) return rv;
rv = AppendSegment(seg);
if (NS_FAILED(rv)) return rv;
nsresult rv = StoreCurveTo(absCoords, x, y, x1, y1, x2, y2);
NS_ENSURE_SUCCESS(rv, rv);
const char* pos = tokenpos;
@ -695,21 +623,8 @@ nsresult nsSVGPathDataParser::matchSmoothCurvetoArgSeq(PRBool absCoords)
float x, y, x2, y2;
ENSURE_MATCHED(matchSmoothCurvetoArg(&x, &y, &x2, &y2));
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothAbs> segAbs;
rv = NS_NewSVGPathSegCurvetoCubicSmoothAbs(getter_AddRefs(segAbs), x, y, x2, y2);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothRel> segRel;
rv = NS_NewSVGPathSegCurvetoCubicSmoothRel(getter_AddRefs(segRel), x, y, x2, y2);
seg = segRel;
}
if (NS_FAILED(rv)) return rv;
rv = AppendSegment(seg);
if (NS_FAILED(rv)) return rv;
nsresult rv = StoreSmoothCurveTo(absCoords, x, y, x2, y2);
NS_ENSURE_SUCCESS(rv, rv);
const char* pos = tokenpos;
@ -777,22 +692,9 @@ nsresult nsSVGPathDataParser::matchQuadBezierCurvetoArgSeq(PRBool absCoords)
while(1) {
float x, y, x1, y1;
ENSURE_MATCHED(matchQuadBezierCurvetoArg(&x, &y, &x1, &y1));
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticAbs> segAbs;
rv = NS_NewSVGPathSegCurvetoQuadraticAbs(getter_AddRefs(segAbs), x, y, x1, y1);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticRel> segRel;
rv = NS_NewSVGPathSegCurvetoQuadraticRel(getter_AddRefs(segRel), x, y, x1, y1);
seg = segRel;
}
if (NS_FAILED(rv)) return rv;
rv = AppendSegment(seg);
if (NS_FAILED(rv)) return rv;
nsresult rv = StoreQuadCurveTo(absCoords, x, y, x1, y1);
NS_ENSURE_SUCCESS(rv, rv);
const char* pos = tokenpos;
@ -860,23 +762,10 @@ nsresult nsSVGPathDataParser::matchSmoothQuadBezierCurvetoArgSeq(PRBool absCoord
while(1) {
float x, y;
ENSURE_MATCHED(matchCoordPair(&x, &y));
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothAbs> segAbs;
rv = NS_NewSVGPathSegCurvetoQuadraticSmoothAbs(getter_AddRefs(segAbs), x, y);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothRel> segRel;
rv = NS_NewSVGPathSegCurvetoQuadraticSmoothRel(getter_AddRefs(segRel), x, y);
seg = segRel;
}
if (NS_FAILED(rv)) return rv;
rv = AppendSegment(seg);
if (NS_FAILED(rv)) return rv;
nsresult rv = StoreSmoothQuadCurveTo(absCoords, x, y);
NS_ENSURE_SUCCESS(rv, rv);
const char* pos = tokenpos;
if (isTokenCommaWspStarter()) {
@ -920,6 +809,7 @@ nsresult nsSVGPathDataParser::matchEllipticalArc()
return NS_OK;
}
nsresult nsSVGPathDataParser::matchEllipticalArcArgSeq(PRBool absCoords)
{
while(1) {
@ -927,24 +817,10 @@ nsresult nsSVGPathDataParser::matchEllipticalArcArgSeq(PRBool absCoords)
PRBool largeArcFlag, sweepFlag;
ENSURE_MATCHED(matchEllipticalArcArg(&x, &y, &r1, &r2, &angle, &largeArcFlag, &sweepFlag));
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegArcAbs> segAbs;
rv = NS_NewSVGPathSegArcAbs(getter_AddRefs(segAbs), x, y, r1, r2, angle,
largeArcFlag, sweepFlag);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegArcRel> segRel;
rv = NS_NewSVGPathSegArcRel(getter_AddRefs(segRel), x, y, r1, r2, angle,
largeArcFlag, sweepFlag);
seg = segRel;
}
if (NS_FAILED(rv)) return rv;
rv = AppendSegment(seg);
if (NS_FAILED(rv)) return rv;
nsresult rv = StoreEllipticalArc(absCoords, x, y, r1, r2, angle,
largeArcFlag, sweepFlag);
NS_ENSURE_SUCCESS(rv, rv);
const char* pos = tokenpos;
@ -1252,3 +1128,673 @@ PRBool nsSVGPathDataParser::isTokenWspStarter()
return (tokentype == WSP);
}
//-----------------------------------------------------------------------
// ---------------------------------------------------------------
// nsSVGPathDataParserToInternal
nsresult
nsSVGPathDataParserToInternal::Parse(const nsAString &aValue)
{
mPathData->Clear();
mPx = mPy = mCx = mCy = mStartX = mStartY = 0;
mNumArguments = mSizeCommandArray = mSizeArgumentArray = 0;
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
nsresult rv = nsSVGPathDataParser::Parse(aValue);
PathFini();
return rv;
}
nsresult
nsSVGPathDataParserToInternal::StoreMoveTo(PRBool absCoords, float x, float y)
{
if (absCoords) {
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS;
} else {
x += mPx;
y += mPy;
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL;
}
return PathMoveTo(x, y);
}
nsresult
nsSVGPathDataParserToInternal::StoreClosePath()
{
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH;
return PathClose();
}
nsresult
nsSVGPathDataParserToInternal::StoreLineTo(PRBool absCoords, float x, float y)
{
if (absCoords) {
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS;
} else {
x += mPx;
y += mPy;
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_REL;
}
return PathLineTo(x, y);
}
nsresult
nsSVGPathDataParserToInternal::StoreHLineTo(PRBool absCoords, float x)
{
if (absCoords) {
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS;
} else {
x += mPx;
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL;
}
return PathLineTo(x, mPy);
}
nsresult
nsSVGPathDataParserToInternal::StoreVLineTo(PRBool absCoords, float y)
{
if (absCoords) {
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS;
} else {
y += mPy;
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL;
}
return PathLineTo(mPx, y);
}
nsresult
nsSVGPathDataParserToInternal::StoreCurveTo(PRBool absCoords,
float x, float y,
float x1, float y1,
float x2, float y2)
{
if (absCoords) {
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS;
} else {
x += mPx; x1 += mPx; x2 += mPx;
y += mPy; y1 += mPy; y2 += mPy;
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL;
}
mCx = x2;
mCy = y2;
return PathCurveTo(x1, y1, x2, y2, x, y);
}
nsresult
nsSVGPathDataParserToInternal::StoreSmoothCurveTo(PRBool absCoords,
float x, float y,
float x2, float y2)
{
float x1, y1;
// first controlpoint = reflection last one about current point
if (mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL ||
mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS ||
mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL ||
mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS ) {
x1 = 2 * mPx - mCx;
y1 = 2 * mPy - mCy;
} else {
x1 = mPx;
y1 = mPy;
}
if (absCoords) {
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;
} else {
x += mPx;
x2 += mPx;
y += mPy;
y2 += mPy;
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL;
}
mCx = x2;
mCy = y2;
return PathCurveTo(x1, y1, x2, y2, x, y);
}
nsresult
nsSVGPathDataParserToInternal::StoreQuadCurveTo(PRBool absCoords,
float x, float y,
float x1, float y1)
{
if (absCoords) {
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS;
} else {
x += mPx;
x1 += mPx;
y += mPy;
y1 += mPy;
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL;
}
float x31, y31, x32, y32;
// conversion of quadratic bezier curve to cubic bezier curve:
x31 = mPx + (x1 - mPx) * 2 / 3;
y31 = mPy + (y1 - mPy) * 2 / 3;
x32 = x1 + (x - x1) / 3;
y32 = y1 + (y - y1) / 3;
mCx = x1;
mCy = y1;
return PathCurveTo(x31, y31, x32, y32, x, y);
}
nsresult
nsSVGPathDataParserToInternal::StoreSmoothQuadCurveTo(PRBool absCoords,
float x, float y)
{
float x1, y1;
// first controlpoint = reflection last one about current point
if (mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL ||
mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS ||
mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL ||
mPrevSeg == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS ) {
x1 = 2 * mPx - mCx;
y1 = 2 * mPy - mCy;
} else {
x1 = mPx;
y1 = mPy;
}
if (absCoords) {
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;
} else {
x += mPx;
y += mPy;
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL;
}
float x31, y31, x32, y32;
// conversion of quadratic bezier curve to cubic bezier curve:
x31 = mPx + (x1 - mPx) * 2 / 3;
y31 = mPy + (y1 - mPy) * 2 / 3;
x32 = x1 + (x - x1) / 3;
y32 = y1 + (y - y1) / 3;
mCx = x1;
mCy = y1;
return PathCurveTo(x31, y31, x32, y32, x, y);
}
static double
CalcVectorAngle(double ux, double uy, double vx, double vy)
{
double ta = atan2(uy, ux);
double tb = atan2(vy, vx);
if (tb >= ta)
return tb-ta;
return 2 * M_PI - (ta-tb);
}
nsresult
nsSVGPathDataParserToInternal::ConvertArcToCurves(float x2, float y2,
float rx, float ry,
float angle,
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
// 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)
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;
// 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);
// c) draw the cubic bezier:
nsresult rv = PathCurveTo(x1+dx1, y1+dy1, xe+dxe, ye+dye, xe, ye);
NS_ENSURE_SUCCESS(rv, rv);
// do next segment
theta1 = theta2;
x1 = (float)xe;
y1 = (float)ye;
}
return NS_OK;
}
nsresult
nsSVGPathDataParserToInternal::StoreEllipticalArc(PRBool absCoords,
float x, float y,
float r1, float r2,
float angle,
PRBool largeArcFlag,
PRBool sweepFlag)
{
if (absCoords) {
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_ARC_ABS;
} else {
x += mPx;
y += mPy;
mPrevSeg = nsIDOMSVGPathSeg::PATHSEG_ARC_REL;
}
return ConvertArcToCurves(x, y, r1, r2, angle, largeArcFlag, sweepFlag);
}
nsresult
nsSVGPathDataParserToInternal::PathEnsureSpace(PRUint32 aNumArgs)
{
const PRUint32 expansionRatio = 2;
if (mPathData->mNumCommands + 1 > mSizeCommandArray) {
PRUint32 newSize = (mSizeCommandArray + 1) * expansionRatio;
PRUint8 *tmp;
tmp = (PRUint8*)realloc(mPathData->mCommands, (newSize + 3) / 4);
if (!tmp)
return NS_ERROR_OUT_OF_MEMORY;
mSizeCommandArray = newSize;
mPathData->mCommands = tmp;
}
if (mNumArguments + aNumArgs > mSizeArgumentArray) {
PRUint32 newSize = (mSizeArgumentArray + aNumArgs) * expansionRatio;
float *tmp;
tmp = (float*)realloc(mPathData->mArguments,
newSize * sizeof(float));
if (!tmp)
return NS_ERROR_OUT_OF_MEMORY;
mSizeArgumentArray = newSize;
mPathData->mArguments = tmp;
}
return NS_OK;
}
void
nsSVGPathDataParserToInternal::PathAddCommandCode(PRUint8 aCommand)
{
PRUint32 offset = mPathData->mNumCommands / 4;
PRUint32 shift = 2 * (mPathData->mNumCommands % 4);
if (shift == 0) {
// make sure we set the byte, to avoid false UMR reports
mPathData->mCommands[offset] = aCommand;
} else {
mPathData->mCommands[offset] |= aCommand << shift;
}
mPathData->mNumCommands++;
}
nsresult
nsSVGPathDataParserToInternal::PathMoveTo(float x, float y)
{
nsresult rv = PathEnsureSpace(2);
NS_ENSURE_SUCCESS(rv, rv);
PathAddCommandCode(nsSVGPathList::MOVETO);
mPathData->mArguments[mNumArguments++] = x;
mPathData->mArguments[mNumArguments++] = y;
mPx = mStartX = x;
mPy = mStartY = y;
return NS_OK;
}
nsresult
nsSVGPathDataParserToInternal::PathLineTo(float x, float y)
{
nsresult rv = PathEnsureSpace(2);
NS_ENSURE_SUCCESS(rv, rv);
PathAddCommandCode(nsSVGPathList::LINETO);
mPathData->mArguments[mNumArguments++] = x;
mPathData->mArguments[mNumArguments++] = y;
mPx = x;
mPy = y;
return NS_OK;
}
nsresult
nsSVGPathDataParserToInternal::PathCurveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
nsresult rv = PathEnsureSpace(6);
NS_ENSURE_SUCCESS(rv, rv);
PathAddCommandCode(nsSVGPathList::CURVETO);
mPathData->mArguments[mNumArguments++] = x1;
mPathData->mArguments[mNumArguments++] = y1;
mPathData->mArguments[mNumArguments++] = x2;
mPathData->mArguments[mNumArguments++] = y2;
mPathData->mArguments[mNumArguments++] = x3;
mPathData->mArguments[mNumArguments++] = y3;
mPx = x3;
mPy = y3;
return NS_OK;
}
nsresult
nsSVGPathDataParserToInternal::PathClose()
{
nsresult rv = PathEnsureSpace(0);
NS_ENSURE_SUCCESS(rv, rv);
PathAddCommandCode(nsSVGPathList::CLOSEPATH);
mPx = mStartX;
mPy = mStartY;
return NS_OK;
}
void
nsSVGPathDataParserToInternal::PathFini()
{
if ((mPathData->mNumCommands + 3) / 4 != mSizeCommandArray / 4) {
PRUint8 *tmp8;
tmp8 = (PRUint8*)realloc(mPathData->mCommands,
(mPathData->mNumCommands + 3) / 4);
if (tmp8)
mPathData->mCommands = tmp8;
}
if (mNumArguments != mSizeCommandArray) {
float *tmp32;
tmp32 = (float*)realloc(mPathData->mArguments,
mNumArguments * sizeof(float));
if (tmp32)
mPathData->mArguments = tmp32;
}
}
// ---------------------------------------------------------------
// nsSVGPathDataParserToDOM
nsresult
nsSVGPathDataParserToDOM::AppendSegment(nsIDOMSVGPathSeg* seg)
{
NS_ADDREF(seg);
mData->AppendElement((void*)seg);
return NS_OK;
}
nsresult
nsSVGPathDataParserToDOM::StoreMoveTo(PRBool absCoords, float x, float y)
{
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegMovetoAbs> segAbs;
rv = NS_NewSVGPathSegMovetoAbs(getter_AddRefs(segAbs), x, y);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegMovetoRel> segRel;
rv = NS_NewSVGPathSegMovetoRel(getter_AddRefs(segRel), x, y);
seg = segRel;
}
NS_ENSURE_SUCCESS(rv, rv);
return AppendSegment(seg);
}
nsresult
nsSVGPathDataParserToDOM::StoreClosePath()
{
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSegClosePath> seg;
rv = NS_NewSVGPathSegClosePath(getter_AddRefs(seg));
NS_ENSURE_SUCCESS(rv, rv);
return AppendSegment(seg);
}
nsresult
nsSVGPathDataParserToDOM::StoreLineTo(PRBool absCoords, float x, float y)
{
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegLinetoAbs> segAbs;
rv = NS_NewSVGPathSegLinetoAbs(getter_AddRefs(segAbs), x, y);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegLinetoRel> segRel;
rv = NS_NewSVGPathSegLinetoRel(getter_AddRefs(segRel), x, y);
seg = segRel;
}
NS_ENSURE_SUCCESS(rv, rv);
return AppendSegment(seg);
}
nsresult
nsSVGPathDataParserToDOM::StoreHLineTo(PRBool absCoords, float x)
{
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalAbs> segAbs;
rv = NS_NewSVGPathSegLinetoHorizontalAbs(getter_AddRefs(segAbs), x);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalRel> segRel;
rv = NS_NewSVGPathSegLinetoHorizontalRel(getter_AddRefs(segRel), x);
seg = segRel;
}
NS_ENSURE_SUCCESS(rv, rv);
return AppendSegment(seg);
}
nsresult
nsSVGPathDataParserToDOM::StoreVLineTo(PRBool absCoords, float y)
{
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalAbs> segAbs;
rv = NS_NewSVGPathSegLinetoVerticalAbs(getter_AddRefs(segAbs), y);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalRel> segRel;
rv = NS_NewSVGPathSegLinetoVerticalRel(getter_AddRefs(segRel), y);
seg = segRel;
}
NS_ENSURE_SUCCESS(rv, rv);
return AppendSegment(seg);
}
nsresult
nsSVGPathDataParserToDOM::StoreCurveTo(PRBool absCoords,
float x, float y,
float x1, float y1,
float x2, float y2)
{
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicAbs> segAbs;
rv = NS_NewSVGPathSegCurvetoCubicAbs(getter_AddRefs(segAbs), x, y, x1, y1, x2, y2);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicRel> segRel;
rv = NS_NewSVGPathSegCurvetoCubicRel(getter_AddRefs(segRel), x, y, x1, y1, x2, y2);
seg = segRel;
}
NS_ENSURE_SUCCESS(rv, rv);
return AppendSegment(seg);
}
nsresult
nsSVGPathDataParserToDOM::StoreSmoothCurveTo(PRBool absCoords,
float x, float y,
float x2, float y2)
{
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothAbs> segAbs;
rv = NS_NewSVGPathSegCurvetoCubicSmoothAbs(getter_AddRefs(segAbs), x, y, x2, y2);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothRel> segRel;
rv = NS_NewSVGPathSegCurvetoCubicSmoothRel(getter_AddRefs(segRel), x, y, x2, y2);
seg = segRel;
}
NS_ENSURE_SUCCESS(rv, rv);
return AppendSegment(seg);
}
nsresult
nsSVGPathDataParserToDOM::StoreQuadCurveTo(PRBool absCoords,
float x, float y,
float x1, float y1)
{
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticAbs> segAbs;
rv = NS_NewSVGPathSegCurvetoQuadraticAbs(getter_AddRefs(segAbs), x, y, x1, y1);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticRel> segRel;
rv = NS_NewSVGPathSegCurvetoQuadraticRel(getter_AddRefs(segRel), x, y, x1, y1);
seg = segRel;
}
NS_ENSURE_SUCCESS(rv, rv);
return AppendSegment(seg);
}
nsresult
nsSVGPathDataParserToDOM::StoreSmoothQuadCurveTo(PRBool absCoords,
float x, float y)
{
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothAbs> segAbs;
rv = NS_NewSVGPathSegCurvetoQuadraticSmoothAbs(getter_AddRefs(segAbs), x, y);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothRel> segRel;
rv = NS_NewSVGPathSegCurvetoQuadraticSmoothRel(getter_AddRefs(segRel), x, y);
seg = segRel;
}
NS_ENSURE_SUCCESS(rv, rv);
return AppendSegment(seg);
}
nsresult
nsSVGPathDataParserToDOM::StoreEllipticalArc(PRBool absCoords,
float x, float y,
float r1, float r2,
float angle,
PRBool largeArcFlag,
PRBool sweepFlag)
{
nsresult rv;
nsCOMPtr<nsIDOMSVGPathSeg> seg;
if (absCoords) {
nsCOMPtr<nsIDOMSVGPathSegArcAbs> segAbs;
rv = NS_NewSVGPathSegArcAbs(getter_AddRefs(segAbs), x, y, r1, r2, angle,
largeArcFlag, sweepFlag);
seg = segAbs;
}
else {
nsCOMPtr<nsIDOMSVGPathSegArcRel> segRel;
rv = NS_NewSVGPathSegArcRel(getter_AddRefs(segRel), x, y, r1, r2, angle,
largeArcFlag, sweepFlag);
seg = segRel;
}
NS_ENSURE_SUCCESS(rv, rv);
return AppendSegment(seg);
}

View File

@ -42,6 +42,9 @@
#include "nsCOMPtr.h"
#include "nsVoidArray.h"
#include "nsIDOMSVGPathSeg.h"
#include <cairo.h>
class nsSVGPathList;
////////////////////////////////////////////////////////////////////////
// nsSVGPathDataParser: a simple recursive descent parser that builds
@ -51,21 +54,34 @@
class nsSVGPathDataParser
{
public:
nsSVGPathDataParser(nsVoidArray* data);
nsresult Parse(const char* str);
virtual nsresult Parse(const nsAString &aValue);
protected:
nsVoidArray *mData;
const char* inputpos;
char tokenval;
const char* tokenpos;
enum { DIGIT, WSP, COMMA, POINT, SIGN, OTHER, END } tokentype;
// Path data storage
virtual nsresult StoreMoveTo(PRBool absCoords, float x, float y) = 0;
virtual nsresult StoreClosePath() = 0;
virtual nsresult StoreLineTo(PRBool absCoords, float x, float y) = 0;
virtual nsresult StoreHLineTo(PRBool absCoords, float x) = 0;
virtual nsresult StoreVLineTo(PRBool absCoords, float y) = 0;
virtual nsresult StoreCurveTo(PRBool absCoords, float x, float y,
float x1, float y1, float x2, float y2) = 0;
virtual nsresult StoreSmoothCurveTo(PRBool absCoords, float x, float y,
float x2, float y2) = 0;
virtual nsresult StoreQuadCurveTo(PRBool absCoords, float x, float y,
float x1, float y1) = 0;
virtual nsresult StoreSmoothQuadCurveTo(PRBool absCoords,
float x, float y) = 0;
virtual nsresult StoreEllipticalArc(PRBool absCoords, float x, float y,
float r1, float r2, float angle,
PRBool largeArcFlag, PRBool sweepFlag) = 0;
// helpers
nsresult AppendSegment(nsIDOMSVGPathSeg* seg);
void getNextToken();
void windBack(const char* pos);
nsresult match(char tok);
@ -158,7 +174,84 @@ protected:
nsresult matchWsp();
PRBool isTokenWspStarter();
};
class nsSVGPathDataParserToInternal : public nsSVGPathDataParser
{
public:
nsSVGPathDataParserToInternal(nsSVGPathList *data) : mPathData(data) {}
virtual nsresult Parse(const nsAString &aValue);
protected:
virtual nsresult StoreMoveTo(PRBool absCoords, float x, float y);
virtual nsresult StoreClosePath();
virtual nsresult StoreLineTo(PRBool absCoords, float x, float y);
virtual nsresult StoreHLineTo(PRBool absCoords, float x);
virtual nsresult StoreVLineTo(PRBool absCoords, float y);
virtual nsresult StoreCurveTo(PRBool absCoords, float x, float y,
float x1, float y1, float x2, float y2);
virtual nsresult StoreSmoothCurveTo(PRBool absCoords, float x, float y,
float x2, float y2);
virtual nsresult StoreQuadCurveTo(PRBool absCoords, float x, float y,
float x1, float y1);
virtual nsresult StoreSmoothQuadCurveTo(PRBool absCoords,
float x, float y);
virtual nsresult StoreEllipticalArc(PRBool absCoords, float x, float y,
float r1, float r2, float angle,
PRBool largeArcFlag, PRBool sweepFlag);
private:
nsSVGPathList *mPathData;
PRUint16 mPrevSeg; // previous segment type for "smooth" segments"
float mPx, mPy; // current point
float mCx, mCy; // last control point for "smooth" segments
float mStartX, mStartY; // start of current subpath, for closepath
// information used to construct PathList
PRUint32 mNumArguments;
PRUint32 mSizeCommandArray;
PRUint32 mSizeArgumentArray;
// Pathdata helpers
nsresult ConvertArcToCurves(float x2, float y2, float rx, float ry,
float angle, PRBool largeArcFlag, PRBool sweepFlag);
nsresult PathEnsureSpace(PRUint32 aNumArgs);
void PathAddCommandCode(PRUint8 aCommand);
nsresult PathMoveTo(float x, float y);
nsresult PathLineTo(float x, float y);
nsresult PathCurveTo(float x1, float y2, float x2, float y2, float x2, float y3);
nsresult PathClose();
void PathFini();
};
class nsSVGPathDataParserToDOM : public nsSVGPathDataParser
{
public:
nsSVGPathDataParserToDOM(nsVoidArray *data) : mData(data) {}
protected:
virtual nsresult StoreMoveTo(PRBool absCoords, float x, float y);
virtual nsresult StoreClosePath();
virtual nsresult StoreLineTo(PRBool absCoords, float x, float y);
virtual nsresult StoreHLineTo(PRBool absCoords, float x);
virtual nsresult StoreVLineTo(PRBool absCoords, float y);
virtual nsresult StoreCurveTo(PRBool absCoords, float x, float y,
float x1, float y1, float x2, float y2);
virtual nsresult StoreSmoothCurveTo(PRBool absCoords, float x, float y,
float x2, float y2);
virtual nsresult StoreQuadCurveTo(PRBool absCoords, float x, float y,
float x1, float y1);
virtual nsresult StoreSmoothQuadCurveTo(PRBool absCoords,
float x, float y);
virtual nsresult StoreEllipticalArc(PRBool absCoords, float x, float y,
float r1, float r2, float angle,
PRBool largeArcFlag, PRBool sweepFlag);
private:
nsresult AppendSegment(nsIDOMSVGPathSeg* seg);
nsVoidArray *mData;
};
#endif // __NS_SVGPATHDATAPARSER_H__

View File

@ -36,57 +36,20 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsSVGGraphicElement.h"
#include "nsSVGAtoms.h"
#include "nsSVGPathSegList.h"
#include "nsIDOMSVGPathElement.h"
#include "nsIDOMSVGAnimatedPathData.h"
#include "nsIDOMSVGPathSeg.h"
#include "nsSVGPathSeg.h"
#include "nsCOMPtr.h"
#include "nsISVGPathFlatten.h"
#include "nsIDocument.h"
#include "nsIFrame.h"
typedef nsSVGGraphicElement nsSVGPathElementBase;
class nsSVGPathElement : public nsSVGPathElementBase,
public nsIDOMSVGPathElement,
public nsIDOMSVGAnimatedPathData
{
protected:
friend nsresult NS_NewSVGPathElement(nsIContent **aResult,
nsINodeInfo *aNodeInfo);
nsSVGPathElement(nsINodeInfo *aNodeInfo);
virtual ~nsSVGPathElement();
nsresult Init();
public:
// interfaces:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMSVGPATHELEMENT
NS_DECL_NSIDOMSVGANIMATEDPATHDATA
// xxx I wish we could use virtual inheritance
NS_FORWARD_NSIDOMNODE_NO_CLONENODE(nsSVGPathElementBase::)
NS_FORWARD_NSIDOMELEMENT(nsSVGPathElementBase::)
NS_FORWARD_NSIDOMSVGELEMENT(nsSVGPathElementBase::)
// nsIContent interface
NS_IMETHODIMP_(PRBool) IsAttributeMapped(const nsIAtom* name) const;
protected:
already_AddRefed<nsISVGPathFlatten> GetPathFlatten();
nsCOMPtr<nsIDOMSVGPathSegList> mSegments;
};
#include "nsSVGPathDataParser.h"
#include "nsSVGPathElement.h"
#include "nsISVGValueUtils.h"
NS_IMPL_NS_NEW_SVG_ELEMENT(Path)
//----------------------------------------------------------------------
// nsISupports methods
@ -108,38 +71,19 @@ NS_INTERFACE_MAP_END_INHERITING(nsSVGPathElementBase)
nsSVGPathElement::nsSVGPathElement(nsINodeInfo* aNodeInfo)
: nsSVGPathElementBase(aNodeInfo)
{
}
nsSVGPathElement::~nsSVGPathElement()
{
}
nsresult
nsSVGPathElement::Init()
{
nsresult rv = nsSVGPathElementBase::Init();
NS_ENSURE_SUCCESS(rv,rv);
// Create mapped properties:
// d #REQUIRED
rv = NS_NewSVGPathSegList(getter_AddRefs(mSegments));
NS_ENSURE_SUCCESS(rv,rv);
rv = AddMappedSVGValue(nsSVGAtoms::d, mSegments);
NS_ENSURE_SUCCESS(rv,rv);
return rv;
if (mSegments)
NS_REMOVE_SVGVALUE_OBSERVER(mSegments);
}
//----------------------------------------------------------------------
// nsIDOMNode methods
NS_IMPL_DOM_CLONENODE_WITH_INIT(nsSVGPathElement)
//----------------------------------------------------------------------
// nsIDOMSVGPathElement methods:
@ -316,12 +260,32 @@ nsSVGPathElement::CreateSVGPathSegCurvetoQuadraticSmoothRel(float x, float y, ns
}
nsresult
nsSVGPathElement::CreatePathSegList()
{
nsresult rv = NS_NewSVGPathSegList(getter_AddRefs(mSegments));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISVGValue> value = do_QueryInterface(mSegments);
nsAutoString d;
if (NS_SUCCEEDED(GetAttr(kNameSpaceID_None, nsGkAtoms::d, d)))
value->SetValueString(d);
NS_ADD_SVGVALUE_OBSERVER(mSegments);
return NS_OK;
}
//----------------------------------------------------------------------
// nsIDOMSVGAnimatedPathData methods:
/* readonly attribute nsIDOMSVGPathSegList pathSegList; */
NS_IMETHODIMP nsSVGPathElement::GetPathSegList(nsIDOMSVGPathSegList * *aPathSegList)
{
nsresult rv = CreatePathSegList();
NS_ENSURE_SUCCESS(rv, rv);
*aPathSegList = mSegments;
NS_ADDREF(*aPathSegList);
return NS_OK;
@ -336,6 +300,9 @@ NS_IMETHODIMP nsSVGPathElement::GetNormalizedPathSegList(nsIDOMSVGPathSegList *
/* readonly attribute nsIDOMSVGPathSegList animatedPathSegList; */
NS_IMETHODIMP nsSVGPathElement::GetAnimatedPathSegList(nsIDOMSVGPathSegList * *aAnimatedPathSegList)
{
nsresult rv = CreatePathSegList();
NS_ENSURE_SUCCESS(rv, rv);
*aAnimatedPathSegList = mSegments;
NS_ADDREF(*aAnimatedPathSegList);
return NS_OK;
@ -361,6 +328,53 @@ nsSVGPathElement::IsAttributeMapped(const nsIAtom* name) const
nsSVGPathElementBase::IsAttributeMapped(name);
}
nsresult
nsSVGPathElement::BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
const nsAString* aValue, PRBool aNotify)
{
if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::d) {
if (mSegments) {
NS_REMOVE_SVGVALUE_OBSERVER(mSegments);
mSegments = nsnull;
}
nsSVGPathDataParserToInternal parser(&mPathData);
nsresult rv = parser.Parse(*aValue);
NS_ENSURE_SUCCESS(rv, rv);
}
return nsSVGPathElementBase::BeforeSetAttr(aNamespaceID, aName,
aValue, aNotify);
}
NS_IMETHODIMP
nsSVGPathElement::DidModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType)
{
nsCOMPtr<nsIDOMSVGPathSegList> list = do_QueryInterface(observable);
if (list && mSegments == list) {
nsCOMPtr<nsISVGValue> value = do_QueryInterface(mSegments);
nsAutoString d;
nsresult rv = value->GetValueString(d);
NS_ENSURE_SUCCESS(rv, rv);
// Want to keep the seglist alive - SetAttr normally invalidates it
nsCOMPtr<nsIDOMSVGPathSegList> deathGrip = mSegments;
mSegments = nsnull;
rv = SetAttr(kNameSpaceID_None, nsGkAtoms::d, d, PR_TRUE);
// Restore seglist
mSegments = deathGrip;
NS_ENSURE_SUCCESS(rv, rv);
}
return nsSVGPathElementBase::DidModifySVGObservable(observable, aModType);
}
//----------------------------------------------------------------------
// implementation helpers:
@ -389,3 +403,49 @@ nsSVGPathElement::GetPathFlatten()
CallQueryInterface(frame, &flattener);
return flattener;
}
//==================================================================
// nsSVGPathList
void
nsSVGPathList::Clear()
{
if (mCommands) {
free(mCommands);
mCommands = nsnull;
}
if (mArguments) {
free(mArguments);
mArguments = nsnull;
}
mNumCommands = 0;
}
void
nsSVGPathList::Playback(cairo_t *aCtx)
{
float *args = mArguments;
for (PRUint32 i = 0; i < mNumCommands; i++) {
PRUint8 command = (mCommands[i / 4] >> (2 * (i % 4))) & 0x3;
switch (command) {
case MOVETO:
cairo_move_to(aCtx, args[0], args[1]);
args += 2;
break;
case LINETO:
cairo_line_to(aCtx, args[0], args[1]);
args += 2;
break;
case CURVETO:
cairo_curve_to(aCtx,
args[0], args[1], args[2], args[3], args[4], args[5]);
args += 6;
break;
case CLOSEPATH:
cairo_close_path(aCtx);
break;
default:
break;
}
}
}

View File

@ -0,0 +1,112 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Mozilla SVG project.
*
* The Initial Developer of the Original Code is
* Crocodile Clips Ltd..
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __NS_SVGPATHELEMENT_H__
#define __NS_SVGPATHELEMENT_H__
#include "nsSVGGraphicElement.h"
#include "nsIDOMSVGPathElement.h"
#include "nsIDOMSVGAnimatedPathData.h"
#include "nsISVGPathFlatten.h"
class nsSVGPathList
{
friend class nsSVGPathDataParserToInternal;
public:
enum { MOVETO, LINETO, CURVETO, CLOSEPATH };
nsSVGPathList() : mCommands(nsnull), mArguments(nsnull), mNumCommands(0) {}
~nsSVGPathList() { Clear(); }
void Playback(cairo_t *aCtx);
protected:
void Clear();
PRUint8 *mCommands;
float *mArguments;
PRUint32 mNumCommands;
};
typedef nsSVGGraphicElement nsSVGPathElementBase;
class nsSVGPathElement : public nsSVGPathElementBase,
public nsIDOMSVGPathElement,
public nsIDOMSVGAnimatedPathData
{
friend class nsSVGPathFrame;
protected:
friend nsresult NS_NewSVGPathElement(nsIContent **aResult,
nsINodeInfo *aNodeInfo);
nsSVGPathElement(nsINodeInfo *aNodeInfo);
virtual ~nsSVGPathElement();
public:
// interfaces:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMSVGPATHELEMENT
NS_DECL_NSIDOMSVGANIMATEDPATHDATA
// xxx I wish we could use virtual inheritance
NS_FORWARD_NSIDOMNODE_NO_CLONENODE(nsSVGPathElementBase::)
NS_FORWARD_NSIDOMELEMENT(nsSVGPathElementBase::)
NS_FORWARD_NSIDOMSVGELEMENT(nsSVGPathElementBase::)
// nsIContent interface
NS_IMETHODIMP_(PRBool) IsAttributeMapped(const nsIAtom* name) const;
// nsISVGValueObserver
NS_IMETHOD DidModifySVGObservable (nsISVGValue* observable,
nsISVGValue::modificationType aModType);
protected:
virtual nsresult BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
const nsAString* aValue, PRBool aNotify);
already_AddRefed<nsISVGPathFlatten> GetPathFlatten();
// Helper for lazily creating pathseg list
nsresult CreatePathSegList();
nsCOMPtr<nsIDOMSVGPathSegList> mSegments;
nsSVGPathList mPathData;
};
#endif

View File

@ -129,11 +129,9 @@ nsSVGPathSegList::SetValueString(const nsAString& aValue)
{
nsresult rv;
char *str = ToNewCString(aValue);
nsVoidArray data;
nsSVGPathDataParser parser(&data);
rv = parser.Parse(str);
nsSVGPathDataParserToDOM parser(&data);
rv = parser.Parse(aValue);
if (NS_SUCCEEDED(rv)) {
WillModify();
@ -157,7 +155,6 @@ nsSVGPathSegList::SetValueString(const nsAString& aValue)
}
}
nsMemory::Free(str);
return rv;
}

View File

@ -49,6 +49,7 @@
#include "nsSVGUtils.h"
#include "nsINameSpaceManager.h"
#include "nsGkAtoms.h"
#include "nsSVGPathElement.h"
class nsSVGPathFrame : public nsSVGPathGeometryFrame,
public nsISVGMarkable,
@ -58,8 +59,6 @@ protected:
friend nsIFrame*
NS_NewSVGPathFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext);
NS_IMETHOD InitSVG();
public:
nsSVGPathFrame(nsStyleContext* aContext) : nsSVGPathGeometryFrame(aContext) {}
@ -97,8 +96,6 @@ public:
private:
NS_IMETHOD_(nsrefcnt) AddRef() { return NS_OK; }
NS_IMETHOD_(nsrefcnt) Release() { return NS_OK; }
nsCOMPtr<nsIDOMSVGPathSegList> mSegments;
};
NS_INTERFACE_MAP_BEGIN(nsSVGPathFrame)
@ -123,21 +120,6 @@ NS_NewSVGPathFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContex
return new (aPresShell) nsSVGPathFrame(aContext);
}
NS_IMETHODIMP
nsSVGPathFrame::InitSVG()
{
nsresult rv = nsSVGPathGeometryFrame::InitSVG();
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDOMSVGAnimatedPathData> anim_data = do_QueryInterface(mContent);
NS_ASSERTION(anim_data,"wrong content element");
anim_data->GetAnimatedPathSegList(getter_AddRefs(mSegments));
NS_ASSERTION(mSegments, "no pathseglist");
if (!mSegments) return NS_ERROR_FAILURE;
return NS_OK;
}
//----------------------------------------------------------------------
// nsISVGFrame methods:
@ -169,480 +151,29 @@ CalcVectorAngle(double ux, double uy, double vx, double vy)
return 2 * M_PI - (ta-tb);
}
static void
ConvertArcToCairo(cairo_t *aCtx, float x, float y, float x2, float y2,
float rx, float ry,
float angle, PRBool largeArcFlag, PRBool sweepFlag)
{
const double radPerDeg = M_PI/180.0;
double x1=x, y1=y;
// 1. 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)
return;
// 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) {
cairo_line_to(aCtx, x2, y2);
return;
}
// 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;
// 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);
// c) draw the cubic bezier:
cairo_curve_to(aCtx, x1+dx1, y1+dy1, xe+dxe, ye+dye, xe, ye);
// do next segment
theta1 = theta2;
x1 = (float)xe;
y1 = (float)ye;
}
}
NS_IMETHODIMP nsSVGPathFrame::ConstructPath(cairo_t *aCtx)
{
PRUint32 count;
mSegments->GetNumberOfItems(&count);
if (count == 0) return NS_OK;
float cx = 0.0f; // current point
float cy = 0.0f;
float cx1 = 0.0f; // last controlpoint (for s,S,t,T)
float cy1 = 0.0f;
PRUint16 lastSegmentType = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
PRUint32 i;
for (i = 0; i < count; ++i) {
nsCOMPtr<nsIDOMSVGPathSeg> segment;
mSegments->GetItem(i, getter_AddRefs(segment));
PRUint16 type = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
segment->GetPathSegType(&type);
PRBool absCoords = PR_FALSE;
switch (type) {
case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH:
{
cairo_close_path(aCtx);
double dx, dy;
cairo_get_current_point(aCtx, &dx, &dy);
cx = (float)dx;
cy = (float)dy;
}
break;
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
{
float x, y;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegMovetoRel> moveseg = do_QueryInterface(segment);
NS_ASSERTION(moveseg, "interface not implemented");
moveseg->GetX(&x);
moveseg->GetY(&y);
x += cx;
y += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegMovetoAbs> moveseg = do_QueryInterface(segment);
NS_ASSERTION(moveseg, "interface not implemented");
moveseg->GetX(&x);
moveseg->GetY(&y);
}
cx = x;
cy = y;
cairo_move_to(aCtx, x, y);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
{
float x, y;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegLinetoRel> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetX(&x);
lineseg->GetY(&y);
x += cx;
y += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegLinetoAbs> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetX(&x);
lineseg->GetY(&y);
}
cx = x;
cy = y;
cairo_line_to(aCtx, x, y);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
{
float x, y, x1, y1, x2, y2;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicRel> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX1(&x1);
curveseg->GetY1(&y1);
curveseg->GetX2(&x2);
curveseg->GetY2(&y2);
x += cx;
y += cy;
x1 += cx;
y1 += cy;
x2 += cx;
y2 += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicAbs> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX1(&x1);
curveseg->GetY1(&y1);
curveseg->GetX2(&x2);
curveseg->GetY2(&y2);
}
cx = x;
cy = y;
cx1 = x2;
cy1 = y2;
cairo_curve_to(aCtx, x1, y1, x2, y2, x, y);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
{
float x, y, x1, y1, x31, y31, x32, y32;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticRel> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX1(&x1);
curveseg->GetY1(&y1);
x += cx;
y += cy;
x1 += cx;
y1 += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticAbs> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX1(&x1);
curveseg->GetY1(&y1);
}
// conversion of quadratic bezier curve to cubic bezier curve:
x31 = cx + (x1 - cx) * 2 / 3;
y31 = cy + (y1 - cy) * 2 / 3;
x32 = x1 + (x - x1) / 3;
y32 = y1 + (y - y1) / 3;
cx = x;
cy = y;
cx1 = x1;
cy1 = y1;
cairo_curve_to(aCtx, x31, y31, x32, y32, x, y);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
{
float x0, y0, x, y, r1, r2, angle;
PRBool largeArcFlag, sweepFlag;
x0 = cx;
y0 = cy;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegArcRel> arcseg = do_QueryInterface(segment);
NS_ASSERTION(arcseg, "interface not implemented");
arcseg->GetX(&x);
arcseg->GetY(&y);
arcseg->GetR1(&r1);
arcseg->GetR2(&r2);
arcseg->GetAngle(&angle);
arcseg->GetLargeArcFlag(&largeArcFlag);
arcseg->GetSweepFlag(&sweepFlag);
x += cx;
y += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegArcAbs> arcseg = do_QueryInterface(segment);
NS_ASSERTION(arcseg, "interface not implemented");
arcseg->GetX(&x);
arcseg->GetY(&y);
arcseg->GetR1(&r1);
arcseg->GetR2(&r2);
arcseg->GetAngle(&angle);
arcseg->GetLargeArcFlag(&largeArcFlag);
arcseg->GetSweepFlag(&sweepFlag);
}
ConvertArcToCairo(aCtx, cx, cy, x, y, r1, r2,
angle, largeArcFlag, sweepFlag);
cx = x;
cy = y;
}
break;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
{
float x;
float y = cy;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalRel> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetX(&x);
x += cx;
} else {
nsCOMPtr<nsIDOMSVGPathSegLinetoHorizontalAbs> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetX(&x);
}
cx = x;
cairo_line_to(aCtx, x, y);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
{
float x = cx;
float y;
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalRel> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetY(&y);
y += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegLinetoVerticalAbs> lineseg = do_QueryInterface(segment);
NS_ASSERTION(lineseg, "interface not implemented");
lineseg->GetY(&y);
}
cy = y;
cairo_line_to(aCtx, x, y);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
{
float x, y, x1, y1, x2, y2;
if (lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS ) {
// the first controlpoint is the reflection of the last one about the current point:
x1 = 2*cx - cx1;
y1 = 2*cy - cy1;
}
else {
// the first controlpoint is equal to the current point:
x1 = cx;
y1 = cy;
}
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothRel> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX2(&x2);
curveseg->GetY2(&y2);
x += cx;
y += cy;
x2 += cx;
y2 += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoCubicSmoothAbs> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
curveseg->GetX2(&x2);
curveseg->GetY2(&y2);
}
cx = x;
cy = y;
cx1 = x2;
cy1 = y2;
cairo_curve_to(aCtx, x1, y1, x2, y2, x, y);
}
break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
absCoords = PR_TRUE;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
{
float x, y, x1, y1, x31, y31, x32, y32;
if (lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL ||
lastSegmentType == nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS ) {
// the first controlpoint is the reflection of the last one about the current point:
x1 = 2*cx - cx1;
y1 = 2*cy - cy1;
}
else {
// the first controlpoint is equal to the current point:
x1 = cx;
y1 = cy;
}
if (!absCoords) {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothRel> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
x += cx;
y += cy;
} else {
nsCOMPtr<nsIDOMSVGPathSegCurvetoQuadraticSmoothAbs> curveseg = do_QueryInterface(segment);
NS_ASSERTION(curveseg, "interface not implemented");
curveseg->GetX(&x);
curveseg->GetY(&y);
}
// conversion of quadratic bezier curve to cubic bezier curve:
x31 = cx + (x1 - cx) * 2 / 3;
y31 = cy + (y1 - cy) * 2 / 3;
x32 = x1 + (x - x1) / 3;
y32 = y1 + (y - y1) / 3;
cx = x;
cy = y;
cx1 = x1;
cy1 = y1;
cairo_curve_to(aCtx, x31, y31, x32, y32, x, y);
}
break;
default:
NS_ASSERTION(1==0, "unknown path segment");
break;
}
lastSegmentType = type;
}
nsSVGPathElement *element = NS_STATIC_CAST(nsSVGPathElement*, mContent);
element->mPathData.Playback(aCtx);
return NS_OK;
}
static float
calcAngle(float ux, float uy, float vx, float vy)
{
float ta = atan2(uy, ux);
float tb = atan2(vy, vx);
if (tb >= ta)
return tb-ta;
return 2*M_PI - (ta-tb);
}
//----------------------------------------------------------------------
// nsISVGMarkable methods:
void
nsSVGPathFrame::GetMarkPoints(nsVoidArray *aMarks) {
nsCOMPtr<nsIDOMSVGPathSegList> segmentList;
nsCOMPtr<nsIDOMSVGAnimatedPathData> anim_data = do_QueryInterface(mContent);
NS_ASSERTION(anim_data,"wrong content element");
anim_data->GetAnimatedPathSegList(getter_AddRefs(segmentList));
NS_ASSERTION(segmentList, "no pathseglist");
if (!segmentList) return;
PRUint32 count;
mSegments->GetNumberOfItems(&count);
segmentList->GetNumberOfItems(&count);
nsCOMPtr<nsIDOMSVGPathSeg> segment;
float cx = 0.0f; // current point
@ -663,7 +194,7 @@ nsSVGPathFrame::GetMarkPoints(nsVoidArray *aMarks) {
PRUint32 i;
for (i = 0; i < count; ++i) {
nsCOMPtr<nsIDOMSVGPathSeg> segment;
mSegments->GetItem(i, getter_AddRefs(segment));
segmentList->GetItem(i, getter_AddRefs(segment));
PRUint16 type = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
segment->GetPathSegType(&type);
@ -871,8 +402,9 @@ nsSVGPathFrame::GetMarkPoints(nsVoidArray *aMarks) {
cyp = -root*r2*xp/r1;
float theta, delta;
theta = calcAngle(1.0, 0.0, (xp-cxp)/r1, (yp-cyp)/r2);
delta = calcAngle((xp-cxp)/r1, (yp-cyp)/r2, (-xp-cxp)/r1, (-yp-cyp)/r2);
theta = CalcVectorAngle(1.0, 0.0, (xp-cxp)/r1, (yp-cyp)/r2);
delta = CalcVectorAngle((xp-cxp)/r1, (yp-cyp)/r2,
(-xp-cxp)/r1, (-yp-cyp)/r2);
if (!sweepFlag && delta > 0)
delta -= 2.0*M_PI;
else if (sweepFlag && delta < 0)