Bug 541882. Add support for SMIL animation of the preserveAspectRatio attribute in SVG. r=dholbert

This commit is contained in:
Jonathan Watt 2010-02-08 02:28:01 +00:00
parent 7f4fbf94ce
commit c9e50cb93c
15 changed files with 260 additions and 26 deletions

View File

@ -1526,6 +1526,18 @@ nsSVGElement::DidChangePreserveAspectRatio(PRBool aDoSetAttr)
newStr, PR_TRUE);
}
void
nsSVGElement::DidAnimatePreserveAspectRatio()
{
nsIFrame* frame = GetPrimaryFrame();
if (frame) {
frame->AttributeChanged(kNameSpaceID_None,
nsGkAtoms::preserveAspectRatio,
nsIDOMMutationEvent::MODIFICATION);
}
}
nsSVGElement::StringAttributesInfo
nsSVGElement::GetStringInfo()
{
@ -1749,6 +1761,12 @@ nsSVGElement::GetAnimatedAttr(const nsIAtom* aName)
}
}
// preserveAspectRatio:
if (aName == nsGkAtoms::preserveAspectRatio) {
nsSVGPreserveAspectRatio *preserveAspectRatio = GetPreserveAspectRatio();
return preserveAspectRatio ? preserveAspectRatio->ToSMILAttr(this) : nsnull;
}
return nsnull;
}

View File

@ -159,6 +159,7 @@ public:
virtual void DidAnimateNumber(PRUint8 aAttrEnum);
virtual void DidAnimateBoolean(PRUint8 aAttrEnum);
virtual void DidAnimateEnum(PRUint8 aAttrEnum);
virtual void DidAnimatePreserveAspectRatio();
void GetAnimatedLengthValues(float *aFirst, ...);
void GetAnimatedNumberValues(float *aFirst, ...);

View File

@ -329,6 +329,12 @@ nsSVGFE::DidAnimateEnum(PRUint8 aAttrEnum)
DidAnimateAttr(this);
}
void
nsSVGFE::DidAnimatePreserveAspectRatio(PRUint8 aAttrEnum)
{
DidAnimateAttr(this);
}
void
nsSVGFE::DidAnimateBoolean(PRUint8 aAttrEnum)
{
@ -5459,7 +5465,8 @@ nsSVGFEImageElement::Filter(nsSVGFilterInstance *instance,
const gfxRect& filterSubregion = aTarget->mFilterPrimitiveSubregion;
gfxMatrix viewBoxTM =
nsSVGUtils::GetViewBoxTransform(filterSubregion.Width(), filterSubregion.Height(),
nsSVGUtils::GetViewBoxTransform(this,
filterSubregion.Width(), filterSubregion.Height(),
0,0, nativeWidth, nativeHeight,
mPreserveAspectRatio);

View File

@ -221,6 +221,7 @@ protected:
virtual void DidAnimateNumber(PRUint8 aAttrEnum);
virtual void DidAnimateEnum(PRUint8 aAttrEnum);
virtual void DidAnimateBoolean(PRUint8 aAttrEnum);
virtual void DidAnimatePreserveAspectRatio(PRUint8 aAttrEnum);
// nsIDOMSVGFitlerPrimitiveStandardAttributes values
enum { X, Y, WIDTH, HEIGHT };

View File

@ -414,7 +414,8 @@ nsSVGMarkerElement::GetViewBoxTransform()
float refY = mLengthAttributes[REFY].GetAnimValue(mCoordCtx);
gfxMatrix viewBoxTM =
nsSVGUtils::GetViewBoxTransform(viewportWidth, viewportHeight,
nsSVGUtils::GetViewBoxTransform(this,
viewportWidth, viewportHeight,
viewbox.x, viewbox.y,
viewbox.width, viewbox.height,
mPreserveAspectRatio,

View File

@ -38,6 +38,12 @@
#include "nsSVGPreserveAspectRatio.h"
#include "nsWhitespaceTokenizer.h"
#ifdef MOZ_SMIL
#include "nsSMILValue.h"
#include "SMILEnumType.h"
#endif // MOZ_SMIL
using namespace mozilla;
////////////////////////////////////////////////////////////////////////
// nsSVGPreserveAspectRatio class
@ -158,28 +164,26 @@ nsSVGPreserveAspectRatio::ToDOMAnimVal(nsIDOMSVGPreserveAspectRatio **aResult,
return NS_OK;
}
nsresult
nsSVGPreserveAspectRatio::SetBaseValueString(const nsAString &aValueAsString,
nsSVGElement *aSVGElement,
PRBool aDoSetAttr)
static nsresult
ToPreserveAspectRatio(const nsAString &aString,
nsSVGPreserveAspectRatio::PreserveAspectRatio *aValue)
{
if (aValueAsString.IsEmpty() ||
NS_IsAsciiWhitespace(aValueAsString[0])) {
if (aString.IsEmpty() || NS_IsAsciiWhitespace(aString[0])) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
nsWhitespaceTokenizer tokenizer(aValueAsString);
nsWhitespaceTokenizer tokenizer(aString);
if (!tokenizer.hasMoreTokens()) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
const nsAString &token = tokenizer.nextToken();
nsresult rv;
PreserveAspectRatio val;
nsSVGPreserveAspectRatio::PreserveAspectRatio val;
val.mDefer = token.EqualsLiteral("defer");
val.SetDefer(token.EqualsLiteral("defer"));
if (val.mDefer) {
if (val.GetDefer()) {
if (!tokenizer.hasMoreTokens()) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
@ -198,15 +202,35 @@ nsSVGPreserveAspectRatio::SetBaseValueString(const nsAString &aValueAsString,
return NS_ERROR_DOM_SYNTAX_ERR;
}
} else {
val.mMeetOrSlice = nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_MEET;
val.SetMeetOrSlice(nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_MEET);
}
if (tokenizer.hasMoreTokens()) {
return NS_ERROR_DOM_SYNTAX_ERR;
}
*aValue = val;
return NS_OK;
}
nsresult
nsSVGPreserveAspectRatio::SetBaseValueString(const nsAString &aValueAsString,
nsSVGElement *aSVGElement,
PRBool aDoSetAttr)
{
PreserveAspectRatio val;
nsresult res = ToPreserveAspectRatio(aValueAsString, &val);
if (NS_FAILED(res)) {
return res;
}
mAnimVal = mBaseVal = val;
aSVGElement->DidChangePreserveAspectRatio(aDoSetAttr);
#ifdef MOZ_SMIL
if (mIsAnimated) {
aSVGElement->AnimationNeedsResample();
}
#endif
return NS_OK;
}
@ -242,7 +266,12 @@ nsSVGPreserveAspectRatio::SetBaseAlign(PRUint16 aAlign,
mAnimVal.mAlign = mBaseVal.mAlign;
aSVGElement->DidChangePreserveAspectRatio(PR_TRUE);
#ifdef MOZ_SMIL
if (mIsAnimated) {
aSVGElement->AnimationNeedsResample();
}
#endif
return NS_OK;
}
@ -255,10 +284,25 @@ nsSVGPreserveAspectRatio::SetBaseMeetOrSlice(PRUint16 aMeetOrSlice,
mAnimVal.mMeetOrSlice = mBaseVal.mMeetOrSlice;
aSVGElement->DidChangePreserveAspectRatio(PR_TRUE);
#ifdef MOZ_SMIL
if (mIsAnimated) {
aSVGElement->AnimationNeedsResample();
}
#endif
return NS_OK;
}
void
nsSVGPreserveAspectRatio::SetAnimValue(PRUint64 aPackedValue, nsSVGElement *aSVGElement)
{
mAnimVal.SetDefer(((aPackedValue & 0xff0000) >> 16) ? PR_TRUE : PR_FALSE);
mAnimVal.SetAlign(PRUint16((aPackedValue & 0xff00) >> 8));
mAnimVal.SetMeetOrSlice(PRUint16(aPackedValue & 0xff));
mIsAnimated = PR_TRUE;
aSVGElement->DidAnimatePreserveAspectRatio();
}
nsresult
nsSVGPreserveAspectRatio::ToDOMAnimatedPreserveAspectRatio(
nsIDOMSVGAnimatedPreserveAspectRatio **aResult,
@ -271,3 +315,67 @@ nsSVGPreserveAspectRatio::ToDOMAnimatedPreserveAspectRatio(
NS_ADDREF(*aResult);
return NS_OK;
}
#ifdef MOZ_SMIL
nsISMILAttr*
nsSVGPreserveAspectRatio::ToSMILAttr(nsSVGElement *aSVGElement)
{
return new SMILPreserveAspectRatio(this, aSVGElement);
}
static PRUint64
PackPreserveAspectRatio(const nsSVGPreserveAspectRatio::PreserveAspectRatio& par)
{
// All preserveAspectRatio values are enum values (do not interpolate), so we
// can safely collate them and treat them as a single enum as for SMIL.
PRUint64 packed = 0;
packed |= PRUint64(par.GetDefer() ? 1 : 0) << 16;
packed |= PRUint64(par.GetAlign()) << 8;
packed |= PRUint64(par.GetMeetOrSlice());
return packed;
}
nsresult
nsSVGPreserveAspectRatio::SMILPreserveAspectRatio
::ValueFromString(const nsAString& aStr,
const nsISMILAnimationElement* /*aSrcElement*/,
nsSMILValue& aValue) const
{
PreserveAspectRatio par;
nsresult res = ToPreserveAspectRatio(aStr, &par);
NS_ENSURE_SUCCESS(res, res);
nsSMILValue val(&SMILEnumType::sSingleton);
val.mU.mUint = PackPreserveAspectRatio(par);
aValue = val;
return NS_OK;
}
nsSMILValue
nsSVGPreserveAspectRatio::SMILPreserveAspectRatio::GetBaseValue() const
{
nsSMILValue val(&SMILEnumType::sSingleton);
val.mU.mUint = PackPreserveAspectRatio(mVal->GetBaseValue());
return val;
}
void
nsSVGPreserveAspectRatio::SMILPreserveAspectRatio::ClearAnimValue()
{
if (mVal->mIsAnimated) {
mVal->SetAnimValue(PackPreserveAspectRatio(mVal->GetBaseValue()), mSVGElement);
mVal->mIsAnimated = PR_FALSE;
}
}
nsresult
nsSVGPreserveAspectRatio::SMILPreserveAspectRatio::SetAnimValue(const nsSMILValue& aValue)
{
NS_ASSERTION(aValue.mType == &SMILEnumType::sSingleton,
"Unexpected type to assign animated value");
if (aValue.mType == &SMILEnumType::sSingleton) {
mVal->SetAnimValue(aValue.mU.mUint, mSVGElement);
}
return NS_OK;
}
#endif // MOZ_SMIL

View File

@ -95,6 +95,7 @@ public:
mBaseVal.mMeetOrSlice = nsIDOMSVGPreserveAspectRatio::SVG_MEETORSLICE_MEET;
mBaseVal.mDefer = PR_FALSE;
mAnimVal = mBaseVal;
mIsAnimated = PR_FALSE;
}
nsresult SetBaseValueString(const nsAString& aValue,
@ -104,19 +105,31 @@ public:
nsresult SetBaseAlign(PRUint16 aAlign, nsSVGElement *aSVGElement);
nsresult SetBaseMeetOrSlice(PRUint16 aMeetOrSlice, nsSVGElement *aSVGElement);
void SetAnimValue(PRUint64 aPackedValue, nsSVGElement *aSVGElement);
const PreserveAspectRatio &GetBaseValue() const
{ return mBaseVal; }
const PreserveAspectRatio &GetAnimValue() const
{ return mAnimVal; }
const PreserveAspectRatio &GetAnimValue(nsSVGElement *aSVGElement) const
{
#ifdef MOZ_SMIL
aSVGElement->FlushAnimations();
#endif
return mAnimVal;
}
nsresult ToDOMAnimatedPreserveAspectRatio(
nsIDOMSVGAnimatedPreserveAspectRatio **aResult,
nsSVGElement* aSVGElement);
#ifdef MOZ_SMIL
// Returns a new nsISMILAttr object that the caller must delete
nsISMILAttr* ToSMILAttr(nsSVGElement* aSVGElement);
#endif // MOZ_SMIL
private:
PreserveAspectRatio mAnimVal;
PreserveAspectRatio mBaseVal;
PRPackedBool mIsAnimated;
nsresult ToDOMBaseVal(nsIDOMSVGPreserveAspectRatio **aResult,
nsSVGElement* aSVGElement);
@ -157,12 +170,12 @@ private:
nsRefPtr<nsSVGElement> mSVGElement;
NS_IMETHOD GetAlign(PRUint16* aAlign)
{ *aAlign = mVal->GetAnimValue().GetAlign(); return NS_OK; }
{ *aAlign = mVal->GetAnimValue(mSVGElement).GetAlign(); return NS_OK; }
NS_IMETHOD SetAlign(PRUint16 aAlign)
{ return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; }
NS_IMETHOD GetMeetOrSlice(PRUint16* aMeetOrSlice)
{ *aMeetOrSlice = mVal->GetAnimValue().GetMeetOrSlice(); return NS_OK; }
{ *aMeetOrSlice = mVal->GetAnimValue(mSVGElement).GetMeetOrSlice(); return NS_OK; }
NS_IMETHOD SetMeetOrSlice(PRUint16 aValue)
{ return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; }
};
@ -185,6 +198,28 @@ private:
{ return mVal->ToDOMAnimVal(aAnimVal, mSVGElement); }
};
#ifdef MOZ_SMIL
struct SMILPreserveAspectRatio : public nsISMILAttr
{
public:
SMILPreserveAspectRatio(nsSVGPreserveAspectRatio* aVal, nsSVGElement* aSVGElement)
: mVal(aVal), mSVGElement(aSVGElement) {}
// These will stay alive because a nsISMILAttr only lives as long
// as the Compositing step, and DOM elements don't get a chance to
// die during that.
nsSVGPreserveAspectRatio* mVal;
nsSVGElement* mSVGElement;
// nsISMILAttr methods
virtual nsresult ValueFromString(const nsAString& aStr,
const nsISMILAnimationElement* aSrcElement,
nsSMILValue& aValue) const;
virtual nsSMILValue GetBaseValue() const;
virtual void ClearAnimValue();
virtual nsresult SetAnimValue(const nsSMILValue& aValue);
};
#endif // MOZ_SMIL
};
#endif //__NS_SVGPRESERVEASPECTRATIO_H__

View File

@ -1000,7 +1000,8 @@ nsSVGSVGElement::GetViewBoxTransform()
return gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
}
return nsSVGUtils::GetViewBoxTransform(viewportWidth, viewportHeight,
return nsSVGUtils::GetViewBoxTransform(this,
viewportWidth, viewportHeight,
viewBox.x, viewBox.y,
viewBox.width, viewBox.height,
mPreserveAspectRatio);
@ -1225,6 +1226,14 @@ nsSVGSVGElement::DidChangePreserveAspectRatio(PRBool aDoSetAttr)
InvalidateTransformNotifyFrame();
}
void
nsSVGSVGElement::DidAnimatePreserveAspectRatio()
{
nsSVGSVGElementBase::DidAnimatePreserveAspectRatio();
InvalidateTransformNotifyFrame();
}
nsSVGPreserveAspectRatio *
nsSVGSVGElement::GetPreserveAspectRatio()
{

View File

@ -197,6 +197,8 @@ public:
virtual void DidChangeViewBox(PRBool aDoSetAttr);
virtual void DidChangePreserveAspectRatio(PRBool aDoSetAttr);
virtual void DidAnimatePreserveAspectRatio();
// nsSVGSVGElement methods:
float GetLength(PRUint8 mCtxType);
float GetMMPerPx(PRUint8 mCtxType = 0);

View File

@ -0,0 +1,38 @@
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="reftest-wait"
onload="setTimeAndSnapshot(1, true)">
<title>Test animation of the "preserveAspectRatio" attribute on the "svg" element</title>
<script xlink:href="smil-util.js" type="text/javascript"/>
<rect width="100%" height="100%" fill="lime"/>
<!-- 40% through animation simple duration -
tests that the animation doesn't affect the element too early -->
<svg width="100" height="50" viewBox="0 0 100 100"
preserveAspectRatio="xMidYMid meet">
<!-- this should change the referencing element at 1.25s,
causing the red rect to stretch to fill its whole viewport -->
<animate attributeName="preserveAspectRatio"
calcMode="discrete"
begin="0s" dur="2.5s"
to="xMidYMid slice"
fill="freeze"/>
<rect width="100%" height="100%" fill="red"/>
</svg>
<rect x="25" width="50" height="50" fill="lime"/>
<!-- 50% through animation simple duration -
tests that the animation affects the element now -->
<rect y="50" width="100" height="50" fill="red"/>
<svg y="50" width="100" height="50" viewBox="0 0 100 100"
preserveAspectRatio="xMidYMid meet">
<!-- this should change the referencing element at 1s,
causing the lime rect to stretch to fill its whole viewport -->
<animate attributeName="preserveAspectRatio"
calcMode="discrete"
begin="0s" dur="2s"
to="xMidYMid slice"
fill="freeze"/>
<rect width="100%" height="100%" fill="lime"/>
</svg>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -88,6 +88,9 @@ fails == anim-fillopacity-1xml.svg anim-standard-ref.svg # bug 534028
# animate some boolean attributes:
== anim-feConvolveMatrix-preserveAlpha-01.svg lime.svg
# animate some preserveAspectRatio attributes
== anim-svg-preserveAspectRatio-01.svg lime.svg
== anim-remove-1.svg anim-standard-ref.svg
== anim-remove-2.svg anim-standard-ref.svg
== anim-remove-3.svg anim-standard-ref.svg

View File

@ -202,7 +202,8 @@ nsSVGImageFrame::GetImageTransform()
mImageContainer->GetHeight(&nativeHeight);
gfxMatrix viewBoxTM =
nsSVGUtils::GetViewBoxTransform(width, height,
nsSVGUtils::GetViewBoxTransform(element,
width, height,
0, 0, nativeWidth, nativeHeight,
element->mPreserveAspectRatio);

View File

@ -459,6 +459,11 @@ nsSVGPatternFrame::GetReferencedPattern()
nsSVGPatternElement *
nsSVGPatternFrame::GetPatternWithAttr(nsIAtom *aAttrName, nsIContent *aDefault)
{
// XXX TODO: this method needs to take account of SMIL animation, since it
// the requested attribute may be animated even if it is not set in the DOM.
// The callers also need to be fixed up to then ask for the right thing from
// the pattern we return! Do we neet to call mContent->FlushAnimations()?
if (mContent->HasAttr(kNameSpaceID_None, aAttrName))
return static_cast<nsSVGPatternElement *>(mContent);
@ -541,7 +546,10 @@ nsSVGPatternFrame::ConstructCTM(const gfxRect &callerBBox,
nsSVGSVGElement *ctx = aTargetContent->GetCtx();
float viewportWidth = GetWidth()->GetAnimValue(ctx);
float viewportHeight = GetHeight()->GetAnimValue(ctx);
gfxMatrix viewBoxTM = nsSVGUtils::GetViewBoxTransform(viewportWidth, viewportHeight,
nsSVGPatternElement *patternElement =
static_cast<nsSVGPatternElement*>(mContent);
gfxMatrix viewBoxTM = nsSVGUtils::GetViewBoxTransform(patternElement,
viewportWidth, viewportHeight,
viewBox.x, viewBox.y,
viewBox.width, viewBox.height,
GetPreserveAspectRatio(),

View File

@ -799,7 +799,8 @@ nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect)
}
gfxMatrix
nsSVGUtils::GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
nsSVGUtils::GetViewBoxTransform(nsSVGElement* aElement,
float aViewportWidth, float aViewportHeight,
float aViewboxX, float aViewboxY,
float aViewboxWidth, float aViewboxHeight,
const nsSVGPreserveAspectRatio &aPreserveAspectRatio,
@ -808,8 +809,8 @@ nsSVGUtils::GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
NS_ASSERTION(aViewboxWidth > 0, "viewBox width must be greater than zero!");
NS_ASSERTION(aViewboxHeight > 0, "viewBox height must be greater than zero!");
PRUint16 align = aPreserveAspectRatio.GetAnimValue().GetAlign();
PRUint16 meetOrSlice = aPreserveAspectRatio.GetAnimValue().GetMeetOrSlice();
PRUint16 align = aPreserveAspectRatio.GetAnimValue(aElement).GetAlign();
PRUint16 meetOrSlice = aPreserveAspectRatio.GetAnimValue(aElement).GetMeetOrSlice();
// default to the defaults
if (align == nsIDOMSVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_UNKNOWN)

View File

@ -344,7 +344,8 @@ public:
/* Generate a viewbox to viewport tranformation matrix */
static gfxMatrix
GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
GetViewBoxTransform(nsSVGElement* aElement,
float aViewportWidth, float aViewportHeight,
float aViewboxX, float aViewboxY,
float aViewboxWidth, float aViewboxHeight,
const nsSVGPreserveAspectRatio &aPreserveAspectRatio,