Bug 436418, patch G: SVG/SMIL animateMotion - add support for path attribute. r=roc

This commit is contained in:
Daniel Holbert 2010-04-28 16:00:54 -07:00
parent 2196ba717a
commit 7b4b55a963
4 changed files with 100 additions and 15 deletions

View File

@ -40,6 +40,9 @@
#include "nsSVGAngle.h"
#include "SVGMotionSMILType.h"
#include "SVGMotionSMILPathUtils.h"
#include "nsSVGPathDataParser.h"
#include "nsSVGPathSeg.h"
#include "nsSVGPathElement.h" // for nsSVGPathList
namespace mozilla {
@ -92,6 +95,12 @@ SVGMotionSMILAnimationFunction::SetAttr(nsIAtom* aAttribute,
if (aParseResult) {
*aParseResult = rv;
}
} else if (aAttribute == nsGkAtoms::path) {
aResult.SetTo(aValue);
if (aParseResult) {
*aParseResult = NS_OK;
}
MarkStaleIfAttributeAffectsPath(aAttribute);
} else if (aAttribute == nsGkAtoms::by ||
aAttribute == nsGkAtoms::from ||
aAttribute == nsGkAtoms::to ||
@ -113,7 +122,8 @@ SVGMotionSMILAnimationFunction::UnsetAttr(nsIAtom* aAttribute)
UnsetKeyPoints();
} else if (aAttribute == nsGkAtoms::rotate) {
UnsetRotate();
} else if (aAttribute == nsGkAtoms::by ||
} else if (aAttribute == nsGkAtoms::path ||
aAttribute == nsGkAtoms::by ||
aAttribute == nsGkAtoms::from ||
aAttribute == nsGkAtoms::to ||
aAttribute == nsGkAtoms::values) {
@ -144,6 +154,8 @@ void
SVGMotionSMILAnimationFunction::
RebuildPathAndVerticesFromBasicAttrs(const nsIContent* aContextElem)
{
NS_ABORT_IF_FALSE(!HasAttr(nsGkAtoms::path),
"Should be using |path| attr if we have it");
NS_ABORT_IF_FALSE(!mPath, "regenerating when we aleady have path");
NS_ABORT_IF_FALSE(mPathVertices.IsEmpty(),
"regenerating when we already have vertices");
@ -212,6 +224,72 @@ SVGMotionSMILAnimationFunction::
}
}
void
SVGMotionSMILAnimationFunction::RebuildPathAndVerticesFromPathAttr()
{
const nsAString& pathSpec = GetAttr(nsGkAtoms::path)->GetStringValue();
mPathSourceType = ePathSourceType_PathAttr;
// Generate gfxFlattenedPath from |path| attr
nsresult rv;
nsSVGPathList pathData;
nsSVGPathDataParserToInternal pathParser(&pathData);
rv = pathParser.Parse(pathSpec);
if (NS_FAILED(rv)) {
// Parse error.
return;
}
mPath = pathData.GetFlattenedPath(gfxMatrix());
// Generate list of vertices from |path| attr
rv = SetPathVerticesFromPathString(pathSpec);
if (NS_FAILED(rv)) {
// The first parser liked our string, but the second did not. (unexpected,
// but possible depending on parser implementations.) Clear path so we
// completely (instead of partially) fail.
mPath = nsnull;
NS_WARNING("nsSVGPathDataParserToInternal successfully parsed path string, but "
"nsSVGPathDataParserToDOM did not");
}
}
nsresult
SVGMotionSMILAnimationFunction::SetPathVerticesFromPathString(const nsAString& aPathSpec)
{
// Parse the string to an array of path segments.
nsCOMArray<nsIDOMSVGPathSeg> pathSegments;
nsSVGPathDataParserToDOM segmentParser(&pathSegments);
nsresult rv = segmentParser.Parse(aPathSpec);
if (NS_FAILED(rv)) {
return rv;
}
// Iterate across the parsed segments to populate our mPathVertices array.
PRUint32 numSegments = pathSegments.Count();
nsSVGPathSegTraversalState ts;
double runningDistTotal = 0.0;
for (PRUint32 i = 0; i < numSegments; ++i) {
nsSVGPathSeg* segment = static_cast<nsSVGPathSeg*>(pathSegments[i]);
PRUint16 type = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
segment->GetPathSegType(&type);
if (i == 0 ||
(type != nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS &&
type != nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL)) {
// Increment running length total (note that MoveTo's have 0 length)
runningDistTotal += segment->GetLength(&ts);
// Add an entry for the current point.
if (!mPathVertices.AppendElement(runningDistTotal)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
}
return NS_OK;
}
// Helper to regenerate our path representation & its list of vertices
void
SVGMotionSMILAnimationFunction::
@ -228,9 +306,10 @@ SVGMotionSMILAnimationFunction::
// through our list of path-defining attributes, in order of priority.
// XXXdholbert This is where we should check for mpath, in <mpath> patch.
// ...else, if no mpath child:
// XXXdholbert This is where we should check for path attr, in |path| patch.
// ...else {
{
if (HasAttr(nsGkAtoms::path)) {
RebuildPathAndVerticesFromPathAttr();
mValueNeedsReparsingEverySample = PR_FALSE;
} else {
// Get path & vertices from basic SMIL attrs: from/by/to/values
RebuildPathAndVerticesFromBasicAttrs(aTargetElement);

View File

@ -88,6 +88,7 @@ protected:
void MarkStaleIfAttributeAffectsPath(nsIAtom* aAttribute);
nsresult SetPathVerticesFromPathString(const nsAString& aPathSpec);
void RebuildPathAndVertices(const nsIContent* aContextElem);
void RebuildPathAndVerticesFromPathAttr();
void RebuildPathAndVerticesFromBasicAttrs(const nsIContent* aContextElem);
PRBool GenerateValuesForPathAndPoints(gfxFlattenedPath* aPath,
PRBool aIsKeyPoints,

View File

@ -514,13 +514,7 @@ nsSVGPathElement::DidModifySVGObservable(nsISVGValue* observable,
already_AddRefed<gfxFlattenedPath>
nsSVGPathElement::GetFlattenedPath(const gfxMatrix &aMatrix)
{
gfxContext ctx(nsSVGUtils::GetThebesComputationalSurface());
ctx.SetMatrix(aMatrix);
mPathData.Playback(&ctx);
ctx.IdentityMatrix();
return ctx.GetFlattenedPath();
return mPathData.GetFlattenedPath(aMatrix);
}
//----------------------------------------------------------------------
@ -992,7 +986,11 @@ nsSVGPathElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks)
aMarks->ElementAt(aMarks->Length() - 1).angle = prevAngle;
}
void
nsSVGPathElement::ConstructPath(gfxContext *aCtx)
{
mPathData.Playback(aCtx);
}
//==================================================================
// nsSVGPathList
@ -1040,8 +1038,14 @@ nsSVGPathList::Playback(gfxContext *aCtx)
}
}
void
nsSVGPathElement::ConstructPath(gfxContext *aCtx)
already_AddRefed<gfxFlattenedPath>
nsSVGPathList::GetFlattenedPath(const gfxMatrix& aMatrix)
{
mPathData.Playback(aCtx);
gfxContext ctx(nsSVGUtils::GetThebesComputationalSurface());
ctx.SetMatrix(aMatrix);
Playback(&ctx);
ctx.IdentityMatrix();
return ctx.GetFlattenedPath();
}

View File

@ -56,6 +56,7 @@ public:
nsSVGPathList() : mArguments(nsnull), mNumCommands(0), mNumArguments(0) {}
~nsSVGPathList() { Clear(); }
void Playback(gfxContext *aCtx);
already_AddRefed<gfxFlattenedPath> GetFlattenedPath(const gfxMatrix &aMatrix);
void Clear();
protected: