diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index 88ec463f2706..769a758e18c1 100755 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -1606,6 +1606,7 @@ GK_ATOM(IBSplitSpecialSibling, "IBSplitSpecialSibling") // nsIFrame* GK_ATOM(lineCursorProperty, "LineCursorProperty") // nsLineBox* GK_ATOM(rowCursorProperty, "RowCursorProperty") // nsTableRowGroupFrame::FrameCursorData* GK_ATOM(maxElementWidthProperty, "MaxElementWidthProperty") // nscoord* +GK_ATOM(outlineInnerRectProperty, "OutlineInnerRectProperty") // nsRect* GK_ATOM(outOfFlowDirtyRectProperty, "OutOfFlowDirtyRectProperty") // nsRect* GK_ATOM(overflowAreaProperty, "OverflowArea") // nsRect* GK_ATOM(overflowProperty, "OverflowProperty") // list of nsIFrame* @@ -1614,6 +1615,7 @@ GK_ATOM(excessOverflowContainersProperty, "ExcessOverflowContainersProperty") // GK_ATOM(overflowLinesProperty, "OverflowLinesProperty") // list of nsLineBox* GK_ATOM(overflowOutOfFlowsProperty, "OverflowOutOfFlowsProperty") // nsFrameList* GK_ATOM(overflowPlaceholdersProperty, "OverflowPlaceholdersProperty") // nsFrameList* +GK_ATOM(preEffectsBBoxProperty, "PreEffectsBBoxProperty") // nsRect* GK_ATOM(rowUnpaginatedHeightProperty, "RowUnpaginatedHeightProperty") // nscoord* GK_ATOM(spaceManagerProperty, "SpaceManagerProperty") // the space manager for a block GK_ATOM(tabWidthProperty, "TabWidthProperty") // nsTArray* array of tab widths diff --git a/content/svg/content/src/nsSVGFilterElement.h b/content/svg/content/src/nsSVGFilterElement.h index 5c86e7799b1e..44af5ab5ccab 100644 --- a/content/svg/content/src/nsSVGFilterElement.h +++ b/content/svg/content/src/nsSVGFilterElement.h @@ -54,6 +54,7 @@ class nsSVGFilterElement : public nsSVGFilterElementBase, public nsIDOMSVGUnitTypes { friend class nsSVGFilterFrame; + friend class nsAutoFilterInstance; protected: friend nsresult NS_NewSVGFilterElement(nsIContent **aResult, diff --git a/content/svg/content/src/nsSVGFilters.h b/content/svg/content/src/nsSVGFilters.h index 94f6abcd87ca..41a542461e95 100644 --- a/content/svg/content/src/nsSVGFilters.h +++ b/content/svg/content/src/nsSVGFilters.h @@ -42,6 +42,7 @@ #include "nsIFrame.h" #include "gfxRect.h" #include "gfxImageSurface.h" +#include "nsIDOMSVGFilters.h" class nsSVGFilterResource; class nsSVGString; diff --git a/content/svg/content/src/nsSVGLength2.cpp b/content/svg/content/src/nsSVGLength2.cpp index 0b0d6ba1d7f6..136df56a3c01 100644 --- a/content/svg/content/src/nsSVGLength2.cpp +++ b/content/svg/content/src/nsSVGLength2.cpp @@ -40,6 +40,8 @@ #include "prdtoa.h" #include "nsTextFormatter.h" #include "nsSVGSVGElement.h" +#include "nsIFrame.h" +#include "nsSVGIntegrationUtils.h" NS_IMPL_ADDREF(nsSVGLength2::DOMBaseVal) NS_IMPL_RELEASE(nsSVGLength2::DOMBaseVal) @@ -179,20 +181,51 @@ nsSVGLength2::GetMMPerPixel(nsSVGSVGElement *aCtx) const return mmPerPx; } +float +nsSVGLength2::GetMMPerPixel(nsIFrame *aNonSVGFrame) const +{ + nsPresContext* presContext = aNonSVGFrame->PresContext(); + float pixelsPerInch = + presContext->AppUnitsToFloatCSSPixels(presContext->AppUnitsPerInch()); + return 25.4f/pixelsPerInch; +} + +static float +FixAxisLength(float aLength) +{ + if (aLength == 0.0f) { + NS_WARNING("zero axis length"); + return 1e-20f; + } + return aLength; +} + float nsSVGLength2::GetAxisLength(nsSVGSVGElement *aCtx) const { if (!aCtx) return 1; - float d = aCtx->GetLength(mCtxType); + return FixAxisLength(aCtx->GetLength(mCtxType)); +} - if (d == 0.0f) { - NS_WARNING("zero axis length"); - d = 1e-20f; +float +nsSVGLength2::GetAxisLength(nsIFrame *aNonSVGFrame) const +{ + gfxRect rect = nsSVGIntegrationUtils::GetSVGRectForNonSVGFrame(aNonSVGFrame); + float length; + switch (mCtxType) { + case nsSVGUtils::X: length = rect.Width(); break; + case nsSVGUtils::Y: length = rect.Height(); break; + case nsSVGUtils::XY: + length = nsSVGUtils::ComputeNormalizedHypotenuse(rect.Width(), rect.Height()); + break; + default: + NS_NOTREACHED("Unknown axis type"); + length = 1; + break; } - - return d; + return FixAxisLength(length); } float @@ -240,6 +273,39 @@ nsSVGLength2::GetUnitScaleFactor(nsSVGSVGElement *aCtx) const } } +float +nsSVGLength2::GetUnitScaleFactor(nsIFrame *aFrame) const +{ + nsIContent* content = aFrame->GetContent(); + if (content->IsNodeOfType(nsINode::eSVG)) + return GetUnitScaleFactor(static_cast(content)); + + switch (mSpecifiedUnitType) { + case nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER: + case nsIDOMSVGLength::SVG_LENGTHTYPE_PX: + return 1; + case nsIDOMSVGLength::SVG_LENGTHTYPE_MM: + return GetMMPerPixel(aFrame); + case nsIDOMSVGLength::SVG_LENGTHTYPE_CM: + return GetMMPerPixel(aFrame) / 10.0f; + case nsIDOMSVGLength::SVG_LENGTHTYPE_IN: + return GetMMPerPixel(aFrame) / 25.4f; + case nsIDOMSVGLength::SVG_LENGTHTYPE_PT: + return GetMMPerPixel(aFrame) * POINTS_PER_INCH_FLOAT / 25.4f; + case nsIDOMSVGLength::SVG_LENGTHTYPE_PC: + return GetMMPerPixel(aFrame) * POINTS_PER_INCH_FLOAT / 24.4f / 12.0f; + case nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE: + return 100.0f / GetAxisLength(aFrame); + case nsIDOMSVGLength::SVG_LENGTHTYPE_EMS: + return 1 / GetEmLength(aFrame); + case nsIDOMSVGLength::SVG_LENGTHTYPE_EXS: + return 1 / GetExLength(aFrame); + default: + NS_NOTREACHED("Unknown unit type"); + return 0; + } +} + void nsSVGLength2::SetBaseValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement) diff --git a/content/svg/content/src/nsSVGLength2.h b/content/svg/content/src/nsSVGLength2.h index 02ce8da32070..90b1c17ceaa9 100644 --- a/content/svg/content/src/nsSVGLength2.h +++ b/content/svg/content/src/nsSVGLength2.h @@ -43,6 +43,8 @@ #include "nsSVGElement.h" #include "nsDOMError.h" +class nsIFrame; + class nsSVGLength2 { @@ -64,10 +66,12 @@ public: void GetBaseValueString(nsAString& aValue); void GetAnimValueString(nsAString& aValue); - float GetBaseValue(nsSVGElement* aSVGElement) + float GetBaseValue(nsSVGElement* aSVGElement) const { return mBaseVal / GetUnitScaleFactor(aSVGElement); } - float GetAnimValue(nsSVGElement* aSVGElement) + float GetAnimValue(nsSVGElement* aSVGElement) const { return mAnimVal / GetUnitScaleFactor(aSVGElement); } + float GetAnimValue(nsIFrame* aFrame) const + { return mAnimVal / GetUnitScaleFactor(aFrame); } PRUint8 GetCtxType() const { return mCtxType; } PRUint8 GetSpecifiedUnitType() const { return mSpecifiedUnitType; } @@ -76,9 +80,9 @@ public: float GetAnimValInSpecifiedUnits() const { return mAnimVal; } float GetBaseValInSpecifiedUnits() const { return mBaseVal; } - float GetBaseValue(nsSVGSVGElement* aCtx) + float GetBaseValue(nsSVGSVGElement* aCtx) const { return mBaseVal / GetUnitScaleFactor(aCtx); } - float GetAnimValue(nsSVGSVGElement* aCtx) + float GetAnimValue(nsSVGSVGElement* aCtx) const { return mAnimVal / GetUnitScaleFactor(aCtx); } nsresult ToDOMAnimatedLength(nsIDOMSVGAnimatedLength **aResult, @@ -93,6 +97,14 @@ private: PRUint8 mCtxType; // X, Y or Unspecified PRPackedBool mIsAnimated; + float GetMMPerPixel(nsIFrame *aNonSVGFrame) const; + float GetAxisLength(nsIFrame *aNonSVGFrame) const; + float GetEmLength(nsIFrame *aFrame) const + { return nsSVGUtils::GetFontSize(aFrame); } + float GetExLength(nsIFrame *aFrame) const + { return nsSVGUtils::GetFontXHeight(aFrame); } + float GetUnitScaleFactor(nsIFrame *aFrame) const; + float GetMMPerPixel(nsSVGSVGElement *aCtx) const; float GetAxisLength(nsSVGSVGElement *aCtx) const; float GetEmLength(nsSVGElement *aSVGElement) const diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 168ca29e8678..36c7b90477c7 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -489,7 +489,7 @@ static nsIFrame* GetSpecialSibling(nsIFrame* aFrame) } static nsIFrame* -GetIBSplitSpecialPrevSibling(nsIFrame* aFrame) +GetIBSplitSpecialPrevSiblingForAnonymousBlock(nsIFrame* aFrame) { NS_PRECONDITION(IsFrameSpecial(aFrame) && !IsInlineFrame(aFrame), "Shouldn't call this"); @@ -608,10 +608,7 @@ FindLastBlock(nsIFrame* aKid) } /* - * Unlike the special (next) sibling, the special previous sibling - * property points only from the anonymous block to the original - * inline that preceded it. DO NOT CHANGE THAT -- the - * GetParentStyleContextFrame code depends on it! It is useful for + * The special-prev-sibling is useful for * finding the "special parent" of a frame (i.e., a frame from which a * good parent style context can be obtained), one looks at the * special previous sibling annotation of the real parent of the frame @@ -7837,7 +7834,8 @@ nsCSSFrameConstructor::AppendFrames(nsFrameConstructorState& aState, nsIContent* content = nsnull; nsStyleContext* styleContext = nsnull; if (!inlineSibling) { - nsIFrame* firstInline = GetIBSplitSpecialPrevSibling(parentFrame); + nsIFrame* firstInline = + GetIBSplitSpecialPrevSiblingForAnonymousBlock(parentFrame); NS_ASSERTION(firstInline, "How did that happen?"); content = firstInline->GetContent(); @@ -12455,8 +12453,11 @@ nsCSSFrameConstructor::ConstructInline(nsFrameConstructorState& aState, SetFrameIsSpecial(aNewFrame, blockFrame); SetFrameIsSpecial(blockFrame, inlineFrame); MarkIBSpecialPrevSibling(blockFrame, aNewFrame); + if (inlineFrame) { + MarkIBSpecialPrevSibling(inlineFrame, blockFrame); + } -#ifdef DEBUG + #ifdef DEBUG if (gNoisyInlineConstruction) { nsIFrameDebug* frameDebug; @@ -12725,8 +12726,8 @@ nsCSSFrameConstructor::WipeContainingBlock(nsFrameConstructorState& aState, // too. nsIFrame* floatContainer = aFrame; do { - floatContainer = - GetFloatContainingBlock(GetIBSplitSpecialPrevSibling(floatContainer)); + floatContainer = GetFloatContainingBlock( + GetIBSplitSpecialPrevSiblingForAnonymousBlock(floatContainer)); if (!floatContainer) { break; } diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index f2bdbf4edf8b..40633626313a 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -642,6 +642,16 @@ nsCSSRendering::PaintBorder(nsPresContext* aPresContext, SN(); } +static nsRect +GetOutlineInnerRect(nsIFrame* aFrame) +{ + nsRect* savedOutlineInnerRect = static_cast + (aFrame->GetProperty(nsGkAtoms::outlineInnerRectProperty)); + if (savedOutlineInnerRect) + return *savedOutlineInnerRect; + return aFrame->GetOverflowRect(); +} + void nsCSSRendering::PaintOutline(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, @@ -671,9 +681,6 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext, // get the radius for our outline GetBorderRadiusTwips(aOutlineStyle.mOutlineRadius, aBorderArea.width, twipsRadii); - nscoord offset; - aOutlineStyle.GetOutlineOffset(offset); - // When the outline property is set on :-moz-anonymous-block or // :-moz-anonyomus-positioned-block pseudo-elements, it inherited that // outline from the inline that was broken because it contained a @@ -690,47 +697,35 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext, frameForArea = frameForArea->GetFirstChild(nsnull); NS_ASSERTION(frameForArea, "anonymous block with no children?"); } while (frameForArea); - nsRect overflowArea; + nsRect innerRect; // relative to aBorderArea.TopLeft() if (frameForArea == aForFrame) { - overflowArea = aForFrame->GetOverflowRect(); + innerRect = GetOutlineInnerRect(aForFrame); } else { for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) { // The outline has already been included in aForFrame's overflow // area, but not in those of its descendants, so we have to // include it. Otherwise we'll end up drawing the outline inside // the border. - nsRect r(frameForArea->GetOverflowRect() + + nsRect r(GetOutlineInnerRect(frameForArea) + frameForArea->GetOffsetTo(aForFrame)); - nscoord delta = PR_MAX(offset + width, 0); - r.Inflate(delta, delta); - overflowArea.UnionRect(overflowArea, r); + innerRect.UnionRect(innerRect, r); } } - nsRect outerRect(overflowArea + aBorderArea.TopLeft()); - nsRect innerRect(outerRect); - if (width + offset >= 0) { - // the overflow area is exactly the outside edge of the outline - innerRect.Deflate(width, width); - } else { - // the overflow area is exactly the rectangle containing the frame and its - // children; we can compute the outline directly - innerRect.Deflate(-offset, -offset); - if (innerRect.width < 0 || innerRect.height < 0) { - return; // Protect against negative outline sizes - } - outerRect = innerRect; - outerRect.Inflate(width, width); - } - + innerRect += aBorderArea.TopLeft(); + nscoord offset; + aOutlineStyle.GetOutlineOffset(offset); + innerRect.Inflate(offset, offset); // If the dirty rect is completely inside the border area (e.g., only the // content is being painted), then we can skip out now // XXX this isn't exactly true for rounded borders, where the inside curves may // encroach into the content area. A safer calculation would be to // shorten insideRect by the radius one each side before performing this test. - if (innerRect.Contains(aDirtyRect)) { + if (innerRect.Contains(aDirtyRect)) return; - } + + nsRect outerRect = innerRect; + outerRect.Inflate(width, width); // Get our conversion values nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1); diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 35ffb60903e0..cea8a32dfd2b 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -51,6 +51,9 @@ #include "nsFrameManager.h" #include "gfxContext.h" #include "nsStyleStructInlines.h" +#ifdef MOZ_SVG +#include "nsSVGIntegrationUtils.h" +#endif nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, PRBool aIsForEvents, PRBool aBuildCaret) @@ -267,6 +270,15 @@ nsDisplayList::FlattenTo(nsTArray* aElements) { } } +nsRect +nsDisplayList::GetBounds(nsDisplayListBuilder* aBuilder) const { + nsRect bounds; + for (nsDisplayItem* i = GetBottom(); i != nsnull; i = i->GetAbove()) { + bounds.UnionRect(bounds, i->GetBounds(aBuilder)); + } + return bounds; +} + void nsDisplayList::OptimizeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion) { @@ -671,11 +683,7 @@ nsDisplayWrapList::HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt, nsRect nsDisplayWrapList::GetBounds(nsDisplayListBuilder* aBuilder) { - nsRect bounds; - for (nsDisplayItem* i = mList.GetBottom(); i != nsnull; i = i->GetAbove()) { - bounds.UnionRect(bounds, i->GetBounds(aBuilder)); - } - return bounds; + return mList.GetBounds(aBuilder); } PRBool @@ -928,3 +936,72 @@ nsDisplayWrapList* nsDisplayClip::WrapWithClone(nsDisplayListBuilder* aBuilder, return new (aBuilder) nsDisplayClip(aItem->GetUnderlyingFrame(), mClippingFrame, aItem, mClip); } + +#ifdef MOZ_SVG +nsDisplaySVGEffects::nsDisplaySVGEffects(nsIFrame* aFrame, nsDisplayList* aList) + : nsDisplayWrapList(aFrame, aList), mEffectsFrame(aFrame), + mBounds(aFrame->GetOverflowRect()) +{ + MOZ_COUNT_CTOR(nsDisplaySVGEffects); +} + +#ifdef NS_BUILD_REFCNT_LOGGING +nsDisplaySVGEffects::~nsDisplaySVGEffects() +{ + MOZ_COUNT_DTOR(nsDisplaySVGEffects); +} +#endif + +PRBool nsDisplaySVGEffects::IsOpaque(nsDisplayListBuilder* aBuilder) +{ + return PR_FALSE; +} + +nsIFrame* +nsDisplaySVGEffects::HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt, + HitTestState* aState) +{ + if (!nsSVGIntegrationUtils::HitTestFrameForEffects(mEffectsFrame, + aPt - aBuilder->ToReferenceFrame(mEffectsFrame))) + return nsnull; + return mList.HitTest(aBuilder, aPt, aState); +} + +void nsDisplaySVGEffects::Paint(nsDisplayListBuilder* aBuilder, + nsIRenderingContext* aCtx, const nsRect& aDirtyRect) +{ + nsSVGIntegrationUtils::PaintFramesWithEffects(aCtx, + mEffectsFrame, aDirtyRect, aBuilder, &mList); +} + +PRBool nsDisplaySVGEffects::OptimizeVisibility(nsDisplayListBuilder* aBuilder, + nsRegion* aVisibleRegion) { + nsRegion vis; + vis.And(*aVisibleRegion, GetBounds(aBuilder)); + nsPoint offset = aBuilder->ToReferenceFrame(mEffectsFrame); + nsRect dirtyRect = nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(mEffectsFrame, + vis.GetBounds() - offset) + offset; + + // Our children may be translucent so we should not allow them to subtract + // area from aVisibleRegion. + nsRegion childrenVisibleRegion(dirtyRect); + nsDisplayWrapList::OptimizeVisibility(aBuilder, &childrenVisibleRegion); + return !vis.IsEmpty(); +} + +PRBool nsDisplaySVGEffects::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) +{ + if (aItem->GetType() != TYPE_SVG_EFFECTS) + return PR_FALSE; + // items for the same content element should be merged into a single + // compositing group + // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplaySVGEffects + if (aItem->GetUnderlyingFrame()->GetContent() != mFrame->GetContent()) + return PR_FALSE; + nsDisplaySVGEffects* other = static_cast(aItem); + mList.AppendToBottom(&other->mList); + mBounds.UnionRect(mBounds, + other->mBounds + other->mEffectsFrame->GetOffsetTo(mEffectsFrame)); + return PR_TRUE; +} +#endif diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 0546e7be8727..25c7fb953505 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -392,6 +392,9 @@ public: TYPE_OUTLINE, TYPE_CLIP, TYPE_OPACITY, +#ifdef MOZ_SVG + TYPE_SVG_EFFECTS, +#endif TYPE_WRAPLIST }; @@ -694,6 +697,10 @@ public: */ void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx, const nsRect& aDirtyRect) const; + /** + * Get the bounds. Takes the union of the bounds of all children. + */ + nsRect GetBounds(nsDisplayListBuilder* aBuilder) const; /** * Find the topmost display item that returns a non-null frame, and return * the frame. @@ -1252,4 +1259,39 @@ private: nsRect mClip; }; +#ifdef MOZ_SVG +/** + * A display item to paint a stacking context with effects + * set by the stacking context root frame's style. + */ +class nsDisplaySVGEffects : public nsDisplayWrapList { +public: + nsDisplaySVGEffects(nsIFrame* aFrame, nsDisplayList* aList); +#ifdef NS_BUILD_REFCNT_LOGGING + virtual ~nsDisplaySVGEffects(); +#endif + + virtual Type GetType() { return TYPE_SVG_EFFECTS; } + virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder); + virtual nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt, + HitTestState* aState); + virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) { + return mBounds + aBuilder->ToReferenceFrame(mEffectsFrame); + } + virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx, + const nsRect& aDirtyRect); + virtual PRBool OptimizeVisibility(nsDisplayListBuilder* aBuilder, + nsRegion* aVisibleRegion); + virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem); + NS_DISPLAY_DECL_NAME("SVGEffects") + + nsIFrame* GetEffectsFrame() { return mEffectsFrame; } + +private: + nsIFrame* mEffectsFrame; + // relative to mEffectsFrame + nsRect mBounds; +}; +#endif + #endif /*NSDISPLAYLIST_H_*/ diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 09b41433e399..b69473471ba6 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -79,6 +79,7 @@ #ifdef MOZ_SVG #include "nsSVGUtils.h" +#include "nsSVGIntegrationUtils.h" #include "nsSVGForeignObjectFrame.h" #include "nsSVGOuterSVGFrame.h" #endif @@ -1037,7 +1038,19 @@ AddItemsToRegion(nsDisplayListBuilder* aBuilder, nsDisplayList* aList, for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) { nsDisplayList* sublist = item->GetList(); if (sublist) { - if (item->GetType() == nsDisplayItem::TYPE_CLIP) { + nsDisplayItem::Type type = item->GetType(); +#ifdef MOZ_SVG + if (type == nsDisplayItem::TYPE_SVG_EFFECTS) { + nsDisplaySVGEffects* effectsItem = static_cast(item); + if (!aBuilder->IsMovingFrame(effectsItem->GetEffectsFrame())) { + // Invalidate the whole thing + nsRect r; + r.IntersectRect(aClipRect, effectsItem->GetBounds(aBuilder)); + aRegion->Or(*aRegion, r); + } + } else +#endif + if (type == nsDisplayItem::TYPE_CLIP) { nsDisplayClip* clipItem = static_cast(item); nsRect clip = aClipRect; // If the clipping frame is moving, then it isn't clipping any @@ -1241,45 +1254,63 @@ nsLayoutUtils::BinarySearchForPosition(nsIRenderingContext* aRendContext, } static void -AddRectsForFrame(nsIFrame* aFrame, nsIFrame* aRelativeTo, - nsLayoutUtils::RectCallback* aCallback) +AddBoxesForFrame(nsIFrame* aFrame, + nsLayoutUtils::BoxCallback* aCallback) { nsIAtom* pseudoType = aFrame->GetStyleContext()->GetPseudoType(); if (pseudoType == nsCSSAnonBoxes::tableOuter) { - AddRectsForFrame(aFrame->GetFirstChild(nsnull), aRelativeTo, - aCallback); + AddBoxesForFrame(aFrame->GetFirstChild(nsnull), aCallback); nsIFrame* kid = aFrame->GetFirstChild(nsGkAtoms::captionList); if (kid) { - AddRectsForFrame(kid, aRelativeTo, aCallback); + AddBoxesForFrame(kid, aCallback); } } else if (pseudoType == nsCSSAnonBoxes::mozAnonymousBlock || pseudoType == nsCSSAnonBoxes::mozAnonymousPositionedBlock || pseudoType == nsCSSAnonBoxes::mozMathMLAnonymousBlock || pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock) { for (nsIFrame* kid = aFrame->GetFirstChild(nsnull); kid; kid = kid->GetNextSibling()) { - AddRectsForFrame(kid, aRelativeTo, aCallback); + AddBoxesForFrame(kid, aCallback); } } else { + aCallback->AddBox(aFrame); + } +} + +void +nsLayoutUtils::GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback) +{ + while (aFrame) { + AddBoxesForFrame(aFrame, aCallback); + aFrame = nsLayoutUtils::GetNextContinuationOrSpecialSibling(aFrame); + } +} + +struct BoxToBorderRect : public nsLayoutUtils::BoxCallback { + nsIFrame* mRelativeTo; + nsLayoutUtils::RectCallback* mCallback; + + BoxToBorderRect(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback) + : mCallback(aCallback), mRelativeTo(aRelativeTo) {} + + virtual void AddBox(nsIFrame* aFrame) { #ifdef MOZ_SVG nsRect r; nsIFrame* outer = nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame, &r); if (outer) { - aCallback->AddRect(r + outer->GetOffsetTo(aRelativeTo)); + mCallback->AddRect(r + outer->GetOffsetTo(mRelativeTo)); } else #endif - aCallback->AddRect(nsRect(aFrame->GetOffsetTo(aRelativeTo), aFrame->GetSize())); + mCallback->AddRect(nsRect(aFrame->GetOffsetTo(mRelativeTo), aFrame->GetSize())); } -} +}; void nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo, RectCallback* aCallback) { - while (aFrame) { - AddRectsForFrame(aFrame, aRelativeTo, aCallback); - aFrame = nsLayoutUtils::GetNextContinuationOrSpecialSibling(aFrame); - } + BoxToBorderRect converter(aRelativeTo, aCallback); + GetAllInFlowBoxes(aFrame, &converter); } struct RectAccumulator : public nsLayoutUtils::RectCallback { @@ -1475,6 +1506,23 @@ nsLayoutUtils::GetNextContinuationOrSpecialSibling(nsIFrame *aFrame) return nsnull; } +nsIFrame* +nsLayoutUtils::GetFirstContinuationOrSpecialSibling(nsIFrame *aFrame) +{ + nsIFrame *result = aFrame->GetFirstContinuation(); + if (result->GetStateBits() & NS_FRAME_IS_SPECIAL) { + while (PR_TRUE) { + nsIFrame *f = static_cast + (result->GetProperty(nsGkAtoms::IBSplitSpecialPrevSibling)); + if (!f) + break; + result = f; + } + } + + return result; +} + PRBool nsLayoutUtils::IsViewportScrollbarFrame(nsIFrame* aFrame) { diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 0929c02ce5b7..5afcf42c0703 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -486,6 +486,19 @@ public: PRInt32& aIndex, PRInt32& aTextWidth); + class BoxCallback { + public: + virtual void AddBox(nsIFrame* aFrame) = 0; + }; + /** + * Collect all CSS boxes associated with aFrame and its + * continuations, "drilling down" through outer table frames and + * some anonymous blocks since they're not real CSS boxes. + * If aFrame is null, no boxes are returned. + * SVG frames return a single box, themselves. + */ + static void GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback); + class RectCallback { public: virtual void AddRect(const nsRect& aRect) = 0; @@ -579,6 +592,13 @@ public: static nsIFrame* GetNextContinuationOrSpecialSibling(nsIFrame *aFrame); + /** + * Get the first frame in the continuation-plus-special-sibling chain + * containing aFrame. + */ + static nsIFrame* + GetFirstContinuationOrSpecialSibling(nsIFrame *aFrame); + /** * Check whether aFrame is a part of the scrollbar or scrollcorner of * the root content. diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 6150364749a0..7ec29c21a1ad 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -120,6 +120,10 @@ #include "nsBlockFrame.h" #include "nsDisplayList.h" +#ifdef MOZ_SVG +#include "nsSVGIntegrationUtils.h" +#endif + #include "gfxContext.h" static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID); @@ -1181,7 +1185,15 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, dirtyRect.IntersectRect(dirtyRect, absPosClip - aBuilder->ToReferenceFrame(this)); } - + +#ifdef MOZ_SVG + PRBool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this); + if (usingSVGEffects) { + dirtyRect = + nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect); + } +#endif + nsDisplayListCollection set; nsresult rv; { @@ -1262,12 +1274,17 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder, resultList.AppendToTop(item); } - if (disp->mOpacity == 1.0f) { - aList->AppendToTop(&resultList); - } else { +#ifdef MOZ_SVG + if (usingSVGEffects) { + rv = aList->AppendNewToTop(new (aBuilder) nsDisplaySVGEffects(this, &resultList)); + } else +#endif + if (disp->mOpacity < 1.0f) { rv = aList->AppendNewToTop(new (aBuilder) nsDisplayOpacity(this, &resultList)); + } else { + aList->AppendToTop(&resultList); } - + return rv; } @@ -1406,7 +1423,11 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, !PresContext()->GetTheme()->WidgetIsContainer(ourDisp->mAppearance)) return NS_OK; - PRBool isComposited = disp->mOpacity != 1.0f; + PRBool isComposited = disp->mOpacity != 1.0f +#ifdef MOZ_SVG + || nsSVGIntegrationUtils::UsingEffectsForFrame(aChild) +#endif + ; PRBool isPositioned = disp->IsPositioned(); if (isComposited || isPositioned || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) { // If you change this, also change IsPseudoStackingContextFromStyle() @@ -3591,6 +3612,15 @@ void nsIFrame::InvalidateInternal(const nsRect& aDamageRect, nscoord aX, nscoord aY, nsIFrame* aForChild, PRBool aImmediate) { +#ifdef MOZ_SVG + if (nsSVGIntegrationUtils::UsingEffectsForFrame(this)) { + nsRect r = nsSVGIntegrationUtils::GetInvalidAreaForChangedSource(this, + aDamageRect + nsPoint(aX, aY)); + GetParent()->InvalidateInternal(r, mRect.x, mRect.y, this, aImmediate); + return; + } +#endif + GetParent()-> InvalidateInternal(aDamageRect, aX + mRect.x, aY + mRect.y, this, aImmediate); } @@ -3620,12 +3650,53 @@ nsIFrame::InvalidateRoot(const nsRect& aDamageRect, view->GetViewManager()->UpdateView(view, aDamageRect + nsPoint(aX, aY), flags); } -static nsRect ComputeOutlineRect(const nsIFrame* aFrame, PRBool* aAnyOutline, - const nsRect& aOverflowRect) { +static void +DestroyRectFunc(void* aFrame, + nsIAtom* aPropertyName, + void* aPropertyValue, + void* aDtorData) +{ + delete static_cast(aPropertyValue); +} + +static void +SetRectProperty(nsIFrame* aFrame, nsIAtom* aProp, const nsRect& aRect) +{ + nsRect* r = new nsRect(aRect); + if (!r) + return; + aFrame->SetProperty(aProp, r, DestroyRectFunc); +} + +static nsRect +ComputeOutlineAndEffectsRect(nsIFrame* aFrame, PRBool* aAnyOutlineOrEffects, + const nsRect& aOverflowRect, + PRBool aStoreRectProperties) { + nsRect r = aOverflowRect; + *aAnyOutlineOrEffects = PR_FALSE; + + // box-shadow + nsCSSShadowArray* boxShadows = aFrame->GetStyleBorder()->mBoxShadow; + if (boxShadows) { + nsRect shadows; + for (PRUint32 i = 0; i < boxShadows->Length(); ++i) { + nsRect tmpRect = r; + nsCSSShadowItem* shadow = boxShadows->ShadowAt(i); + nscoord xOffset = shadow->mXOffset.GetCoordValue(); + nscoord yOffset = shadow->mYOffset.GetCoordValue(); + nscoord outsetRadius = shadow->mRadius.GetCoordValue() + + shadow->mSpread.GetCoordValue(); + + tmpRect.MoveBy(nsPoint(xOffset, yOffset)); + tmpRect.Inflate(outsetRadius, outsetRadius); + + shadows.UnionRect(shadows, tmpRect); + } + r.UnionRect(r, shadows); + } + const nsStyleOutline* outline = aFrame->GetStyleOutline(); PRUint8 outlineStyle = outline->GetOutlineStyle(); - nsRect r = aOverflowRect; - *aAnyOutline = PR_FALSE; if (outlineStyle != NS_STYLE_BORDER_STYLE_NONE) { nscoord width; #ifdef DEBUG @@ -3634,13 +3705,35 @@ static nsRect ComputeOutlineRect(const nsIFrame* aFrame, PRBool* aAnyOutline, outline->GetOutlineWidth(width); NS_ASSERTION(result, "GetOutlineWidth had no cached outline width"); if (width > 0) { + if (aStoreRectProperties) { + SetRectProperty(aFrame, nsGkAtoms::outlineInnerRectProperty, r); + } + nscoord offset; outline->GetOutlineOffset(offset); nscoord inflateBy = PR_MAX(width + offset, 0); r.Inflate(inflateBy, inflateBy); - *aAnyOutline = PR_TRUE; + *aAnyOutlineOrEffects = PR_TRUE; } } + + // Note that we don't remove the outlineInnerRect if a frame loses outline + // style. That would require an extra property lookup for every frame, + // or a new frame state bit to track whether a property had been stored, + // or something like that. It's not worth doing that here. At most it's + // only one heap-allocated rect per frame and it will be cleaned up when + // the frame dies. + +#ifdef MOZ_SVG + if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) { + *aAnyOutlineOrEffects = PR_TRUE; + if (aStoreRectProperties) { + SetRectProperty(aFrame, nsGkAtoms::preEffectsBBoxProperty, r); + } + r = nsSVGIntegrationUtils::ComputeFrameEffectsRect(aFrame, r); + } +#endif + return r; } @@ -3699,10 +3792,10 @@ nsIFrame::CheckInvalidateSizeChange(const nsRect& aOldRect, // invalidated) // Invalidate the entire old frame+outline if the frame has an outline - PRBool anyOutline; - nsRect r = ComputeOutlineRect(this, &anyOutline, - aNewDesiredSize.mOverflowArea); - if (anyOutline) { + PRBool anyOutlineOrEffects; + nsRect r = ComputeOutlineAndEffectsRect(this, &anyOutlineOrEffects, + aOldOverflowRect, PR_FALSE); + if (anyOutlineOrEffects) { r.UnionRect(aOldOverflowRect, r); Invalidate(r); return; @@ -5261,16 +5354,6 @@ nsFrame::GetAccessible(nsIAccessible** aAccessible) } #endif -// Destructor function for the overflow area property -static void -DestroyRectFunc(void* aFrame, - nsIAtom* aPropertyName, - void* aPropertyValue, - void* aDtorData) -{ - delete static_cast(aPropertyValue); -} - /** Create or retrieve the previously stored overflow area, if the frame does * not overflow and no creation is required return nsnull. * @param aCreateIfNecessary create a new nsRect for the overflow area @@ -5314,29 +5397,11 @@ nsRect nsIFrame::GetAdditionalOverflow(const nsRect& aOverflowArea, const nsSize& aNewSize) { - nsRect overflowRect; - // outline - PRBool hasOutline; - overflowRect = ComputeOutlineRect(this, &hasOutline, aOverflowArea); - - // box-shadow - nsCSSShadowArray* boxShadows = GetStyleBorder()->mBoxShadow; - if (boxShadows) { - for (PRUint32 i = 0; i < boxShadows->Length(); ++i) { - nsRect tmpRect(nsPoint(0, 0), aNewSize); - nsCSSShadowItem* shadow = boxShadows->ShadowAt(i); - nscoord xOffset = shadow->mXOffset.GetCoordValue(); - nscoord yOffset = shadow->mYOffset.GetCoordValue(); - nscoord outsetRadius = shadow->mRadius.GetCoordValue() + - shadow->mSpread.GetCoordValue(); - - tmpRect.MoveBy(nsPoint(xOffset, yOffset)); - tmpRect.Inflate(outsetRadius, outsetRadius); - - overflowRect.UnionRect(overflowRect, tmpRect); - } - } + PRBool hasOutlineOrEffects; + nsRect overflowRect = + ComputeOutlineAndEffectsRect(this, &hasOutlineOrEffects, + aOverflowArea, PR_TRUE); // Absolute position clipping PRBool hasAbsPosClip; @@ -5437,18 +5502,25 @@ nsFrame::GetParentStyleContextFrame(nsPresContext* aPresContext, * as aSpecialSibling. This is needed because the split inline's * style context is the parent of the anonymous block's srtyle context. * - * If aFrame is not the anonymous block, aSpecialSibling is not - * touched. + * If aFrame is not the anonymous block, aSpecialSibling is set to null. */ static nsresult -GetIBSpecialSibling(nsPresContext* aPresContext, - nsIFrame* aFrame, - nsIFrame** aSpecialSibling) +GetIBSpecialSiblingForAnonymousBlock(nsPresContext* aPresContext, + nsIFrame* aFrame, + nsIFrame** aSpecialSibling) { NS_PRECONDITION(aFrame, "Must have a non-null frame!"); NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL, "GetIBSpecialSibling should not be called on a non-special frame"); - + + nsIAtom* type = aFrame->GetStyleContext()->GetPseudoType(); + if (type != nsCSSAnonBoxes::mozAnonymousBlock && + type != nsCSSAnonBoxes::mozAnonymousPositionedBlock) { + // it's not the anonymous block + *aSpecialSibling = nsnull; + return NS_OK; + } + // Find the first-in-flow of the frame. (Ugh. This ends up // being O(N^2) when it is called O(N) times.) aFrame = aFrame->GetFirstInFlow(); @@ -5532,10 +5604,11 @@ nsFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent, if (parent->GetStateBits() & NS_FRAME_IS_SPECIAL) { nsIFrame* sibling; nsresult rv = - GetIBSpecialSibling(parent->PresContext(), parent, &sibling); + GetIBSpecialSiblingForAnonymousBlock(parent->PresContext(), parent, &sibling); if (NS_FAILED(rv)) { - // If GetIBSpecialSibling fails, then what? we used to return what is - // now |aProspectiveParent|, but maybe |parent| would make more sense? + // If GetIBSpecialSiblingForAnonymousBlock fails, then what? + // we used to return what is now |aProspectiveParent|, but maybe + // |parent| would make more sense? NS_NOTREACHED("Shouldn't get here"); return aProspectiveParent; } @@ -5594,10 +5667,11 @@ nsFrame::DoGetParentStyleContextFrame(nsPresContext* aPresContext, * If this frame is the anonymous block created when an inline * with a block inside it got split, then the parent style context * is on the first of the three special frames. We can get to it - * using GetIBSpecialSibling + * using GetIBSpecialSiblingForAnonymousBlock */ if (mState & NS_FRAME_IS_SPECIAL) { - nsresult rv = GetIBSpecialSibling(aPresContext, this, aProviderFrame); + nsresult rv = + GetIBSpecialSiblingForAnonymousBlock(aPresContext, this, aProviderFrame); if (NS_FAILED(rv)) { NS_NOTREACHED("Shouldn't get here"); *aProviderFrame = nsnull; diff --git a/layout/reftests/reftest.list b/layout/reftests/reftest.list index 262cc12bb804..e81e6420ce93 100644 --- a/layout/reftests/reftest.list +++ b/layout/reftests/reftest.list @@ -84,6 +84,9 @@ include pagination/reftest.list # svg/ include svg/reftest.list +# svg-integration/ +include svg-integration/reftest.list + # table-background/ include table-background/reftest.list diff --git a/layout/reftests/svg-integration/clipPath-html-01-ref.svg b/layout/reftests/svg-integration/clipPath-html-01-ref.svg new file mode 100644 index 000000000000..1b7c0561107a --- /dev/null +++ b/layout/reftests/svg-integration/clipPath-html-01-ref.svg @@ -0,0 +1,8 @@ + + + + diff --git a/layout/reftests/svg-integration/clipPath-html-01.xhtml b/layout/reftests/svg-integration/clipPath-html-01.xhtml new file mode 100644 index 000000000000..3b879c5097eb --- /dev/null +++ b/layout/reftests/svg-integration/clipPath-html-01.xhtml @@ -0,0 +1,17 @@ + + + +
+ + + + + + + + diff --git a/layout/reftests/svg-integration/clipPath-html-02-ref.svg b/layout/reftests/svg-integration/clipPath-html-02-ref.svg new file mode 100644 index 000000000000..c58637a32223 --- /dev/null +++ b/layout/reftests/svg-integration/clipPath-html-02-ref.svg @@ -0,0 +1,9 @@ + + + + + diff --git a/layout/reftests/svg-integration/clipPath-html-02.xhtml b/layout/reftests/svg-integration/clipPath-html-02.xhtml new file mode 100644 index 000000000000..716c8eaacb6a --- /dev/null +++ b/layout/reftests/svg-integration/clipPath-html-02.xhtml @@ -0,0 +1,20 @@ + + + +
+
+
+
+ + + + + + + + diff --git a/layout/reftests/svg-integration/clipPath-html-03-ref.svg b/layout/reftests/svg-integration/clipPath-html-03-ref.svg new file mode 100644 index 000000000000..70d15f5abec0 --- /dev/null +++ b/layout/reftests/svg-integration/clipPath-html-03-ref.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/layout/reftests/svg-integration/clipPath-html-03.xhtml b/layout/reftests/svg-integration/clipPath-html-03.xhtml new file mode 100644 index 000000000000..3f64dc68dba5 --- /dev/null +++ b/layout/reftests/svg-integration/clipPath-html-03.xhtml @@ -0,0 +1,21 @@ + + + +
+
+
+
+ + + + + + + + + diff --git a/layout/reftests/svg-integration/clipPath-html-04-ref.xhtml b/layout/reftests/svg-integration/clipPath-html-04-ref.xhtml new file mode 100644 index 000000000000..21eae295a77f --- /dev/null +++ b/layout/reftests/svg-integration/clipPath-html-04-ref.xhtml @@ -0,0 +1,14 @@ + + + +
+ + + diff --git a/layout/reftests/svg-integration/clipPath-html-04.xhtml b/layout/reftests/svg-integration/clipPath-html-04.xhtml new file mode 100644 index 000000000000..90c099d694d9 --- /dev/null +++ b/layout/reftests/svg-integration/clipPath-html-04.xhtml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + diff --git a/layout/reftests/svg-integration/clipPath-html-05-ref.xhtml b/layout/reftests/svg-integration/clipPath-html-05-ref.xhtml new file mode 100644 index 000000000000..6efb48a2e08b --- /dev/null +++ b/layout/reftests/svg-integration/clipPath-html-05-ref.xhtml @@ -0,0 +1,14 @@ + + + +
+ + + diff --git a/layout/reftests/svg-integration/clipPath-html-05.xhtml b/layout/reftests/svg-integration/clipPath-html-05.xhtml new file mode 100644 index 000000000000..ae8bf4242d24 --- /dev/null +++ b/layout/reftests/svg-integration/clipPath-html-05.xhtml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + diff --git a/layout/reftests/svg-integration/clipPath-html-06-ref.xhtml b/layout/reftests/svg-integration/clipPath-html-06-ref.xhtml new file mode 100644 index 000000000000..c6b52cf2a7b3 --- /dev/null +++ b/layout/reftests/svg-integration/clipPath-html-06-ref.xhtml @@ -0,0 +1,20 @@ + + + + + + + + +
+ + + + diff --git a/layout/reftests/svg-integration/clipPath-html-06.xhtml b/layout/reftests/svg-integration/clipPath-html-06.xhtml new file mode 100644 index 000000000000..dcb90a39a322 --- /dev/null +++ b/layout/reftests/svg-integration/clipPath-html-06.xhtml @@ -0,0 +1,26 @@ + + + + + + + + +
+ + + + + + + + + + diff --git a/layout/reftests/svg-integration/filter-html-01-ref.svg b/layout/reftests/svg-integration/filter-html-01-ref.svg new file mode 100644 index 000000000000..d0712a290a50 --- /dev/null +++ b/layout/reftests/svg-integration/filter-html-01-ref.svg @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/layout/reftests/svg-integration/filter-html-01.xhtml b/layout/reftests/svg-integration/filter-html-01.xhtml new file mode 100644 index 000000000000..530c97bba1b8 --- /dev/null +++ b/layout/reftests/svg-integration/filter-html-01.xhtml @@ -0,0 +1,20 @@ + + + +
+ + + + + + + + + + + diff --git a/layout/reftests/svg-integration/mask-html-01-ref.svg b/layout/reftests/svg-integration/mask-html-01-ref.svg new file mode 100644 index 000000000000..ca30d428820e --- /dev/null +++ b/layout/reftests/svg-integration/mask-html-01-ref.svg @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/layout/reftests/svg-integration/mask-html-01.xhtml b/layout/reftests/svg-integration/mask-html-01.xhtml new file mode 100644 index 000000000000..c402eb8c4a02 --- /dev/null +++ b/layout/reftests/svg-integration/mask-html-01.xhtml @@ -0,0 +1,22 @@ + + + +
+ + + + + + + + + + + + + diff --git a/layout/reftests/svg-integration/reftest.list b/layout/reftests/svg-integration/reftest.list new file mode 100644 index 000000000000..002dbae1c721 --- /dev/null +++ b/layout/reftests/svg-integration/reftest.list @@ -0,0 +1,9 @@ +== clipPath-html-01.xhtml clipPath-html-01-ref.svg +== clipPath-html-02.xhtml clipPath-html-02-ref.svg +== clipPath-html-03.xhtml clipPath-html-03-ref.svg +== clipPath-html-04.xhtml clipPath-html-04-ref.xhtml +== clipPath-html-05.xhtml clipPath-html-05-ref.xhtml +== clipPath-html-06.xhtml clipPath-html-06-ref.xhtml +== filter-html-01.xhtml filter-html-01-ref.svg +== mask-html-01.xhtml mask-html-01-ref.svg + diff --git a/layout/style/ua.css b/layout/style/ua.css index 9f1412fb78c7..6b4f09b8d4c3 100644 --- a/layout/style/ua.css +++ b/layout/style/ua.css @@ -128,6 +128,9 @@ /* we currently inherit from the inline that is split */ outline: inherit; -moz-outline-offset: inherit; + clip-path: inherit; + filter: inherit; + mask: inherit; } *|*::-moz-xul-anonymous-block { diff --git a/layout/svg/base/src/Makefile.in b/layout/svg/base/src/Makefile.in index ed82c6d49993..db1efec56276 100644 --- a/layout/svg/base/src/Makefile.in +++ b/layout/svg/base/src/Makefile.in @@ -80,6 +80,7 @@ CPPSRCS = \ nsSVGGradientFrame.cpp \ nsSVGImageFrame.cpp \ nsSVGInnerSVGFrame.cpp \ + nsSVGIntegrationUtils.cpp \ nsSVGLeafFrame.cpp \ nsSVGMarkerFrame.cpp \ nsSVGMaskFrame.cpp \ @@ -103,6 +104,7 @@ include $(topsrcdir)/config/config.mk FORCE_STATIC_LIB = 1 EXPORTS = \ + nsSVGIntegrationUtils.h \ nsSVGUtils.h \ nsSVGFilterInstance.h \ nsSVGForeignObjectFrame.h \ diff --git a/layout/svg/base/src/nsISVGChildFrame.h b/layout/svg/base/src/nsISVGChildFrame.h index 9dc3cc97ce44..5d8837e4acdc 100644 --- a/layout/svg/base/src/nsISVGChildFrame.h +++ b/layout/svg/base/src/nsISVGChildFrame.h @@ -51,8 +51,8 @@ class nsIDOMSVGMatrix; class nsSVGRenderState; #define NS_ISVGCHILDFRAME_IID \ -{ 0xe4ecddbf, 0xde7c, 0x4cd9, \ - { 0x92, 0x4a, 0xfa, 0x81, 0xba, 0x83, 0x26, 0x69 } } +{ 0x8b80b2a0, 0x2e1f, 0x4775, \ + { 0xab, 0x47, 0xbe, 0xeb, 0x4b, 0x81, 0x63, 0x6d } } class nsISVGChildFrame : public nsISupports { public: @@ -98,6 +98,7 @@ public: // Set whether we should stop multiplying matrices when building up // the current transformation matrix at this frame. NS_IMETHOD SetMatrixPropagation(PRBool aPropagate)=0; + virtual PRBool GetMatrixPropagation()=0; // Set the current transformation matrix to a particular matrix. // Value is only used if matrix propagation is prevented diff --git a/layout/svg/base/src/nsSVGClipPathFrame.cpp b/layout/svg/base/src/nsSVGClipPathFrame.cpp index 61c5c78a719a..f5cb33291cba 100644 --- a/layout/svg/base/src/nsSVGClipPathFrame.cpp +++ b/layout/svg/base/src/nsSVGClipPathFrame.cpp @@ -61,7 +61,7 @@ NS_NewSVGClipPathFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleCo nsresult nsSVGClipPathFrame::ClipPaint(nsSVGRenderState* aContext, - nsISVGChildFrame* aParent, + nsIFrame* aParent, nsIDOMSVGMatrix *aMatrix) { // If the flag is set when we get here, it means this clipPath frame @@ -103,7 +103,7 @@ nsSVGClipPathFrame::ClipPaint(nsSVGRenderState* aContext, } PRBool -nsSVGClipPathFrame::ClipHitTest(nsISVGChildFrame* aParent, +nsSVGClipPathFrame::ClipHitTest(nsIFrame* aParent, nsIDOMSVGMatrix *aMatrix, const nsPoint &aPoint) { diff --git a/layout/svg/base/src/nsSVGClipPathFrame.h b/layout/svg/base/src/nsSVGClipPathFrame.h index d15ecbe3895e..9190ee62ace1 100644 --- a/layout/svg/base/src/nsSVGClipPathFrame.h +++ b/layout/svg/base/src/nsSVGClipPathFrame.h @@ -54,10 +54,10 @@ protected: public: // nsSVGClipPathFrame methods: nsresult ClipPaint(nsSVGRenderState* aContext, - nsISVGChildFrame* aParent, + nsIFrame* aParent, nsIDOMSVGMatrix *aMatrix); - PRBool ClipHitTest(nsISVGChildFrame* aParent, + PRBool ClipHitTest(nsIFrame* aParent, nsIDOMSVGMatrix *aMatrix, const nsPoint &aPoint); @@ -100,7 +100,7 @@ public: nsSVGClipPathFrame *mFrame; }; - nsISVGChildFrame *mClipParent; + nsIFrame *mClipParent; nsCOMPtr mClipParentMatrix; // nsSVGContainerFrame methods: diff --git a/layout/svg/base/src/nsSVGContainerFrame.cpp b/layout/svg/base/src/nsSVGContainerFrame.cpp index 9f6f16a8bedd..78bb09672dae 100644 --- a/layout/svg/base/src/nsSVGContainerFrame.cpp +++ b/layout/svg/base/src/nsSVGContainerFrame.cpp @@ -88,7 +88,7 @@ nsSVGContainerFrame::Init(nsIContent* aContent, nsIFrame* aParent, nsIFrame* aPrevInFlow) { - AddStateBits(NS_STATE_SVG_NONDISPLAY_CHILD); + AddStateBits(NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_PROPAGATE_TRANSFORM); nsresult rv = nsSVGContainerFrameBase::Init(aContent, aParent, aPrevInFlow); return rv; } @@ -98,6 +98,7 @@ nsSVGDisplayContainerFrame::Init(nsIContent* aContent, nsIFrame* aParent, nsIFrame* aPrevInFlow) { + AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM); if (!(GetStateBits() & NS_STATE_IS_OUTER_SVG)) { AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD); } @@ -277,3 +278,20 @@ nsSVGDisplayContainerFrame::GetBBox(nsIDOMSVGRect **_retval) { return nsSVGUtils::GetBBox(&mFrames, _retval); } + +NS_IMETHODIMP +nsSVGDisplayContainerFrame::SetMatrixPropagation(PRBool aPropagate) +{ + if (aPropagate) { + AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM); + } else { + RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM); + } + return NS_OK; +} + +PRBool +nsSVGDisplayContainerFrame::GetMatrixPropagation() +{ + return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0; +} diff --git a/layout/svg/base/src/nsSVGContainerFrame.h b/layout/svg/base/src/nsSVGContainerFrame.h index b1c4f0127453..6871f962739d 100644 --- a/layout/svg/base/src/nsSVGContainerFrame.h +++ b/layout/svg/base/src/nsSVGContainerFrame.h @@ -109,7 +109,8 @@ public: virtual void NotifySVGChanged(PRUint32 aFlags); NS_IMETHOD NotifyRedrawSuspended(); NS_IMETHOD NotifyRedrawUnsuspended(); - NS_IMETHOD SetMatrixPropagation(PRBool aPropagate) { return NS_ERROR_FAILURE; } + NS_IMETHOD SetMatrixPropagation(PRBool aPropagate); + virtual PRBool GetMatrixPropagation(); NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM) { return NS_ERROR_FAILURE; } virtual already_AddRefed GetOverrideCTM() { return nsnull; } NS_IMETHOD GetBBox(nsIDOMSVGRect **_retval); diff --git a/layout/svg/base/src/nsSVGEffects.cpp b/layout/svg/base/src/nsSVGEffects.cpp index 5ef7b67230ba..c4c99b97a989 100644 --- a/layout/svg/base/src/nsSVGEffects.cpp +++ b/layout/svg/base/src/nsSVGEffects.cpp @@ -127,7 +127,7 @@ nsSVGFilterProperty::UpdateRect() { nsSVGFilterFrame *filter = GetFilterFrame(nsnull); if (filter) { - mFilterRect = filter->GetInvalidationRegion(mFrame, mFrame->GetRect()); + mFilterRect = filter->GetFilterBBox(mFrame, nsnull); } else { mFilterRect = nsRect(); } diff --git a/layout/svg/base/src/nsSVGFilterFrame.cpp b/layout/svg/base/src/nsSVGFilterFrame.cpp index 0a323355170b..a0b9921cd541 100644 --- a/layout/svg/base/src/nsSVGFilterFrame.cpp +++ b/layout/svg/base/src/nsSVGFilterFrame.cpp @@ -42,11 +42,13 @@ #include "nsGkAtoms.h" #include "nsSVGUtils.h" #include "nsSVGFilterElement.h" -#include "nsSVGFilterInstance.h" #include "nsSVGFilters.h" #include "gfxASurface.h" #include "gfxContext.h" #include "gfxImageSurface.h" +#include "nsSVGFilterPaintCallback.h" +#include "nsSVGRect.h" +#include "nsSVGFilterInstance.h" nsIFrame* NS_NewSVGFilterFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext) @@ -78,63 +80,68 @@ MapDeviceRectToFilterSpace(const gfxMatrix& aMatrix, return rect; } -nsresult -nsSVGFilterFrame::CreateInstance(nsISVGChildFrame *aTarget, - const nsIntRect *aDirtyOutputRect, - const nsIntRect *aDirtyInputRect, - nsSVGFilterInstance **aInstance) +class NS_STACK_CLASS nsAutoFilterInstance { +public: + nsAutoFilterInstance(nsIFrame *aTarget, + nsSVGFilterFrame *aFilterFrame, + nsSVGFilterPaintCallback *aPaint, + const nsIntRect *aDirtyOutputRect, + const nsIntRect *aDirtyInputRect, + const nsIntRect *aOverrideSourceBBox); + ~nsAutoFilterInstance(); + + // If this returns null, then draw nothing. Either the filter draws + // nothing or it is "in error". + nsSVGFilterInstance* get() { return mInstance; } + +private: + nsAutoPtr mInstance; + // Store mTarget separately even though mInstance has it, because if + // mInstance creation fails we still need to be able to clean up + nsISVGChildFrame* mTarget; +}; + +nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget, + nsSVGFilterFrame *aFilterFrame, + nsSVGFilterPaintCallback *aPaint, + const nsIntRect *aDirtyOutputRect, + const nsIntRect *aDirtyInputRect, + const nsIntRect *aOverrideSourceBBox) { - *aInstance = nsnull; + nsCOMPtr ctm = nsSVGUtils::GetCanvasTM(aTarget); - nsIFrame *frame; - CallQueryInterface(aTarget, &frame); + CallQueryInterface(aTarget, &mTarget); + if (mTarget) { + mTarget->SetMatrixPropagation(PR_FALSE); + mTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION | + nsISVGChildFrame::TRANSFORM_CHANGED); + } - nsCOMPtr ctm = nsSVGUtils::GetCanvasTM(frame); - - nsSVGElement *target = static_cast(frame->GetContent()); - - aTarget->SetMatrixPropagation(PR_FALSE); - aTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION | - nsISVGChildFrame::TRANSFORM_CHANGED); - - nsSVGFilterElement *filter = static_cast(mContent); - - float x, y, width, height; - nsCOMPtr bbox; - aTarget->GetBBox(getter_AddRefs(bbox)); - - nsSVGLength2 *tmpX, *tmpY, *tmpWidth, *tmpHeight; - tmpX = &filter->mLengthAttributes[nsSVGFilterElement::X]; - tmpY = &filter->mLengthAttributes[nsSVGFilterElement::Y]; - tmpWidth = &filter->mLengthAttributes[nsSVGFilterElement::WIDTH]; - tmpHeight = &filter->mLengthAttributes[nsSVGFilterElement::HEIGHT]; + nsSVGFilterElement *filter = static_cast( + aFilterFrame->GetContent()); PRUint16 units = filter->mEnumAttributes[nsSVGFilterElement::FILTERUNITS].GetAnimValue(); - - // Compute filter effects region as per spec - if (units == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { - if (!bbox) - return NS_OK; - - bbox->GetX(&x); - x += nsSVGUtils::ObjectSpace(bbox, tmpX); - bbox->GetY(&y); - y += nsSVGUtils::ObjectSpace(bbox, tmpY); - width = nsSVGUtils::ObjectSpace(bbox, tmpWidth); - height = nsSVGUtils::ObjectSpace(bbox, tmpHeight); + nsCOMPtr bbox; + if (aOverrideSourceBBox) { + NS_NewSVGRect(getter_AddRefs(bbox), + aOverrideSourceBBox->x, aOverrideSourceBBox->y, + aOverrideSourceBBox->width, aOverrideSourceBBox->height); } else { - x = nsSVGUtils::UserSpace(target, tmpX); - y = nsSVGUtils::UserSpace(target, tmpY); - width = nsSVGUtils::UserSpace(target, tmpWidth); - height = nsSVGUtils::UserSpace(target, tmpHeight); + bbox = nsSVGUtils::GetBBox(aTarget); } - + if (!bbox && units == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) + return; + + gfxRect filterArea = nsSVGUtils::GetRelativeRect(units, + &filter->mLengthAttributes[nsSVGFilterElement::X], bbox, aTarget); + filterArea.RoundOut(); + PRBool resultOverflows; gfxIntSize filterRes; // Compute size of filter buffer - if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::filterRes)) { + if (filter->HasAttr(kNameSpaceID_None, nsGkAtoms::filterRes)) { PRInt32 filterResX, filterResY; filter->GetAnimatedIntegerValues(&filterResX, &filterResY, nsnull); @@ -148,26 +155,21 @@ nsSVGFilterFrame::CreateInstance(nsISVGChildFrame *aTarget, #endif filterRes = - nsSVGUtils::ConvertToSurfaceSize(gfxSize(width, height) * scale, + nsSVGUtils::ConvertToSurfaceSize(filterArea.size * scale, &resultOverflows); } // 0 disables rendering, < 0 is error if (filterRes.width <= 0 || filterRes.height <= 0) - return NS_OK; - -#ifdef DEBUG_tor - fprintf(stderr, "filter bbox: %f,%f %fx%f\n", x, y, width, height); - fprintf(stderr, "filterRes: %u %u\n", filterRes.width, filterRes.height); -#endif + return; // 'fini' is the matrix we will finally use to transform filter space // to surface space for drawing nsCOMPtr scale, fini; NS_NewSVGMatrix(getter_AddRefs(scale), - width / filterRes.width, 0.0f, - 0.0f, height / filterRes.height, - x, y); + filterArea.Width() / filterRes.width, 0.0f, + 0.0f, filterArea.Height() / filterRes.height, + filterArea.X(), filterArea.Y()); ctm->Multiply(scale, getter_AddRefs(fini)); gfxMatrix finiM = nsSVGUtils::ConvertSVGMatrixToThebes(fini); @@ -182,101 +184,122 @@ nsSVGFilterFrame::CreateInstance(nsISVGChildFrame *aTarget, // Setup instance data PRUint16 primitiveUnits = filter->mEnumAttributes[nsSVGFilterElement::PRIMITIVEUNITS].GetAnimValue(); - *aInstance = new nsSVGFilterInstance(aTarget, mContent, bbox, - gfxRect(x, y, width, height), - nsIntSize(filterRes.width, filterRes.height), - fini, - dirtyOutputRect, dirtyInputRect, - primitiveUnits); - return *aInstance ? NS_OK : NS_ERROR_OUT_OF_MEMORY; + mInstance = new nsSVGFilterInstance(aTarget, aPaint, filter, bbox, filterArea, + nsIntSize(filterRes.width, filterRes.height), + fini, + dirtyOutputRect, dirtyInputRect, + primitiveUnits); } -static void -RestoreTargetState(nsISVGChildFrame *aTarget) +nsAutoFilterInstance::~nsAutoFilterInstance() { - aTarget->SetOverrideCTM(nsnull); - aTarget->SetMatrixPropagation(PR_TRUE); - aTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION | + if (!mTarget) + return; + + mTarget->SetOverrideCTM(nsnull); + mTarget->SetMatrixPropagation(PR_TRUE); + mTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION | nsISVGChildFrame::TRANSFORM_CHANGED); } nsresult nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext, - nsISVGChildFrame *aTarget, + nsIFrame *aTarget, + nsSVGFilterPaintCallback *aPaintCallback, const nsIntRect *aDirtyRect) { - nsAutoPtr instance; - nsresult rv = CreateInstance(aTarget, aDirtyRect, nsnull, getter_Transfers(instance)); + nsAutoFilterInstance instance(aTarget, this, aPaintCallback, + aDirtyRect, nsnull, nsnull); + if (!instance.get()) + return NS_OK; - if (NS_SUCCEEDED(rv) && instance) { - // Transformation from user space to filter space - nsCOMPtr filterTransform = - instance->GetUserSpaceToFilterSpaceTransform(); - aTarget->SetOverrideCTM(filterTransform); - aTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION | - nsISVGChildFrame::TRANSFORM_CHANGED); - - nsRefPtr result; - rv = instance->Render(getter_AddRefs(result)); - if (NS_SUCCEEDED(rv) && result) { - nsSVGUtils::CompositeSurfaceMatrix(aContext->GetGfxContext(), - result, instance->GetFilterSpaceToDeviceSpaceTransform(), 1.0); - } - } - - RestoreTargetState(aTarget); - - if (NS_FAILED(rv)) { - aTarget->PaintSVG(aContext, nsnull); + nsRefPtr result; + nsresult rv = instance.get()->Render(getter_AddRefs(result)); + if (NS_SUCCEEDED(rv) && result) { + nsSVGUtils::CompositeSurfaceMatrix(aContext->GetGfxContext(), + result, instance.get()->GetFilterSpaceToDeviceSpaceTransform(), 1.0); } return rv; } -nsRect -nsSVGFilterFrame::GetInvalidationRegion(nsIFrame *aTarget, const nsRect& aRect) +static nsresult +TransformFilterSpaceToDeviceSpace(nsSVGFilterInstance *aInstance, nsIntRect *aRect) { - nsISVGChildFrame *svg; - CallQueryInterface(aTarget, &svg); - - nscoord p2a = aTarget->PresContext()->AppUnitsPerDevPixel(); - nsRect result = aRect; - nsIntRect rect = aRect; - rect.ScaleRoundOut(1.0/p2a); - - nsAutoPtr instance; - nsresult rv = CreateInstance(svg, nsnull, &rect, getter_Transfers(instance)); - if (NS_SUCCEEDED(rv)) { - if (!instance) { - // The filter draws nothing, so nothing is dirty. - result = nsRect(); - } else { - // We've passed in the source's dirty area so the instance knows about it. - // Now we can ask the instance to compute the area of the filter output - // that's dirty. - nsIntRect filterSpaceDirtyRect; - rv = instance->ComputeOutputDirtyRect(&filterSpaceDirtyRect); - if (NS_SUCCEEDED(rv)) { - gfxMatrix m = nsSVGUtils::ConvertSVGMatrixToThebes( - instance->GetFilterSpaceToDeviceSpaceTransform()); - gfxRect r(filterSpaceDirtyRect.x, filterSpaceDirtyRect.y, - filterSpaceDirtyRect.width, filterSpaceDirtyRect.height); - r = m.TransformBounds(r); - r.RoundOut(); - nsIntRect deviceRect; - rv = nsSVGUtils::GfxRectToIntRect(r, &deviceRect); - if (NS_SUCCEEDED(rv)) { - deviceRect.ScaleRoundOut(p2a); - result = deviceRect; - } - } - } - } - - RestoreTargetState(svg); - - return result; + gfxMatrix m = nsSVGUtils::ConvertSVGMatrixToThebes( + aInstance->GetFilterSpaceToDeviceSpaceTransform()); + gfxRect r(aRect->x, aRect->y, aRect->width, aRect->height); + r = m.TransformBounds(r); + r.RoundOut(); + nsIntRect deviceRect; + nsresult rv = nsSVGUtils::GfxRectToIntRect(r, &deviceRect); + if (NS_FAILED(rv)) + return rv; + *aRect = deviceRect; + return NS_OK; } +nsIntRect +nsSVGFilterFrame::GetInvalidationBBox(nsIFrame *aTarget, const nsIntRect& aRect) +{ + nsAutoFilterInstance instance(aTarget, this, nsnull, nsnull, &aRect, nsnull); + if (!instance.get()) + return nsIntRect(); + + // We've passed in the source's dirty area so the instance knows about it. + // Now we can ask the instance to compute the area of the filter output + // that's dirty. + nsIntRect dirtyRect; + nsresult rv = instance.get()->ComputeOutputDirtyRect(&dirtyRect); + if (NS_SUCCEEDED(rv)) { + rv = TransformFilterSpaceToDeviceSpace(instance.get(), &dirtyRect); + if (NS_SUCCEEDED(rv)) + return dirtyRect; + } + + return nsIntRect(); +} + +nsIntRect +nsSVGFilterFrame::GetSourceForInvalidArea(nsIFrame *aTarget, const nsIntRect& aRect) +{ + nsAutoFilterInstance instance(aTarget, this, nsnull, &aRect, nsnull, nsnull); + if (!instance.get()) + return nsIntRect(); + + // Now we can ask the instance to compute the area of the source + // that's needed. + nsIntRect neededRect; + nsresult rv = instance.get()->ComputeSourceNeededRect(&neededRect); + if (NS_SUCCEEDED(rv)) { + rv = TransformFilterSpaceToDeviceSpace(instance.get(), &neededRect); + if (NS_SUCCEEDED(rv)) + return neededRect; + } + + return nsIntRect(); +} + +nsIntRect +nsSVGFilterFrame::GetFilterBBox(nsIFrame *aTarget, const nsIntRect *aSourceBBox) +{ + nsAutoFilterInstance instance(aTarget, this, nsnull, nsnull, nsnull, aSourceBBox); + if (!instance.get()) + return nsIntRect(); + + // We've passed in the source's bounding box so the instance knows about + // it. Now we can ask the instance to compute the bounding box of + // the filter output. + nsIntRect bbox; + nsresult rv = instance.get()->ComputeOutputBBox(&bbox); + if (NS_SUCCEEDED(rv)) { + rv = TransformFilterSpaceToDeviceSpace(instance.get(), &bbox); + if (NS_SUCCEEDED(rv)) + return bbox; + } + + return nsIntRect(); +} + nsIAtom * nsSVGFilterFrame::GetType() const { diff --git a/layout/svg/base/src/nsSVGFilterFrame.h b/layout/svg/base/src/nsSVGFilterFrame.h index 13fd9a593c48..027b27412c53 100644 --- a/layout/svg/base/src/nsSVGFilterFrame.h +++ b/layout/svg/base/src/nsSVGFilterFrame.h @@ -40,7 +40,8 @@ #include "nsRect.h" #include "nsSVGContainerFrame.h" -class nsSVGFilterInstance; +class nsSVGRenderState; +class nsSVGFilterPaintCallback; typedef nsSVGContainerFrame nsSVGFilterFrameBase; class nsSVGFilterFrame : public nsSVGFilterFrameBase @@ -50,17 +51,33 @@ class nsSVGFilterFrame : public nsSVGFilterFrameBase protected: nsSVGFilterFrame(nsStyleContext* aContext) : nsSVGFilterFrameBase(aContext) {} -public: +public: nsresult FilterPaint(nsSVGRenderState *aContext, - nsISVGChildFrame *aTarget, + nsIFrame *aTarget, nsSVGFilterPaintCallback *aPaintCallback, const nsIntRect* aDirtyRect); - // Returns invalidation region for filter (can be bigger than the - // referencing geometry to filter region sizing) in app units - // relative to the origin of the outer svg. - // aRect is the area that would be invalidated. Normally you'd just pass - // aTarget->GetRect() here. - nsRect GetInvalidationRegion(nsIFrame *aTarget, const nsRect& aRect); + /** + * Returns the area that could change when the given rect of the source changes. + * The rectangles are relative to the origin of the outer svg, if aTarget is SVG, + * relative to aTarget itself otherwise, in device pixels. + */ + nsIntRect GetInvalidationBBox(nsIFrame *aTarget, const nsIntRect& aRect); + + /** + * Returns the area in device pixels that is needed from the source when + * the given area needs to be repainted. + * The rectangles are relative to the origin of the outer svg, if aTarget is SVG, + * relative to aTarget itself otherwise, in device pixels. + */ + nsIntRect GetSourceForInvalidArea(nsIFrame *aTarget, const nsIntRect& aRect); + + /** + * Returns the bounding box of the post-filter area of aTarget. + * The rectangles are relative to the origin of the outer svg, if aTarget is SVG, + * relative to aTarget itself otherwise, in device pixels. + * @param aSourceBBox overrides the normal bbox for the source, if non-null + */ + nsIntRect GetFilterBBox(nsIFrame *aTarget, const nsIntRect *aSourceBBox); /** * Get the "type" of the frame @@ -68,13 +85,6 @@ public: * @see nsGkAtoms::svgFilterFrame */ virtual nsIAtom* GetType() const; - -private: - // implementation helpers - nsresult CreateInstance(nsISVGChildFrame *aTarget, - const nsIntRect *aDirtyOutputRect, - const nsIntRect *aDirtyInputRect, - nsSVGFilterInstance **aInstance); }; #endif diff --git a/layout/svg/base/src/nsSVGFilterInstance.cpp b/layout/svg/base/src/nsSVGFilterInstance.cpp index 2536a7dbdd61..016d539d514f 100644 --- a/layout/svg/base/src/nsSVGFilterInstance.cpp +++ b/layout/svg/base/src/nsSVGFilterInstance.cpp @@ -38,6 +38,9 @@ #include "nsSVGUtils.h" #include "nsIDOMSVGUnitTypes.h" #include "nsSVGMatrix.h" +#include "gfxPlatform.h" +#include "nsSVGFilterPaintCallback.h" +#include "nsSVGFilterElement.h" static double Square(double aX) { @@ -48,10 +51,11 @@ float nsSVGFilterInstance::GetPrimitiveLength(nsSVGLength2 *aLength) const { float value; - if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) + if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { value = nsSVGUtils::ObjectSpace(mTargetBBox, aLength); - else - value = nsSVGUtils::UserSpace(TargetElement(), aLength); + } else { + value = nsSVGUtils::UserSpace(mTargetFrame, aLength); + } switch (aLength->GetCtxType()) { case nsSVGUtils::X: @@ -123,29 +127,9 @@ nsSVGFilterInstance::ComputeFilterPrimitiveSubregion(PrimitiveInfo* aPrimitive) gfxRect(0, 0, mFilterSpaceSize.width, mFilterSpaceSize.height); } - nsSVGLength2 *tmpX, *tmpY, *tmpWidth, *tmpHeight; - tmpX = &fE->mLengthAttributes[nsSVGFE::X]; - tmpY = &fE->mLengthAttributes[nsSVGFE::Y]; - tmpWidth = &fE->mLengthAttributes[nsSVGFE::WIDTH]; - tmpHeight = &fE->mLengthAttributes[nsSVGFE::HEIGHT]; - - float x, y, width, height; - if (mPrimitiveUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { - mTargetBBox->GetX(&x); - x += nsSVGUtils::ObjectSpace(mTargetBBox, tmpX); - mTargetBBox->GetY(&y); - y += nsSVGUtils::ObjectSpace(mTargetBBox, tmpY); - width = nsSVGUtils::ObjectSpace(mTargetBBox, tmpWidth); - height = nsSVGUtils::ObjectSpace(mTargetBBox, tmpHeight); - } else { - nsSVGElement* targetElement = TargetElement(); - x = nsSVGUtils::UserSpace(targetElement, tmpX); - y = nsSVGUtils::UserSpace(targetElement, tmpY); - width = nsSVGUtils::UserSpace(targetElement, tmpWidth); - height = nsSVGUtils::UserSpace(targetElement, tmpHeight); - } - - gfxRect region = UserSpaceToFilterSpace(gfxRect(x, y, width, height)); + gfxRect feArea = nsSVGUtils::GetRelativeRect(mPrimitiveUnits, + &fE->mLengthAttributes[nsSVGFE::X], mTargetBBox, mTargetFrame); + gfxRect region = UserSpaceToFilterSpace(feArea); if (!fE->HasAttr(kNameSpaceID_None, nsGkAtoms::x)) region.pos.x = defaultFilterSubregion.X(); @@ -340,19 +324,52 @@ nsSVGFilterInstance::ComputeUnionOfAllNeededBoxes() nsresult nsSVGFilterInstance::BuildSourceImages() { - if (mSourceColorAlpha.mResultNeededBox.IsEmpty() && - mSourceAlpha.mResultNeededBox.IsEmpty()) + nsIntRect neededRect; + neededRect.UnionRect(mSourceColorAlpha.mResultNeededBox, + mSourceAlpha.mResultNeededBox); + if (neededRect.IsEmpty()) return NS_OK; nsRefPtr sourceColorAlpha = CreateImage(); if (!sourceColorAlpha) return NS_ERROR_OUT_OF_MEMORY; - nsSVGRenderState tmpState(sourceColorAlpha); - nsresult rv = mTargetFrame->PaintSVG(&tmpState, nsnull); - if (NS_FAILED(rv)) - return rv; + { + // Paint to an offscreen surface first, then copy it to an image + // surface. This can be faster especially when the stuff we're painting + // contains native themes. + nsRefPtr offscreen = + gfxPlatform::GetPlatform()->CreateOffscreenSurface( + gfxIntSize(mSurfaceRect.width, mSurfaceRect.height), + gfxASurface::ImageFormatARGB32); + if (!offscreen || offscreen->CairoStatus()) + return NS_ERROR_OUT_OF_MEMORY; + offscreen->SetDeviceOffset(gfxPoint(-mSurfaceRect.x, -mSurfaceRect.y)); + + nsSVGRenderState tmpState(offscreen); + nsCOMPtr userSpaceToFilterSpaceTransform + = GetUserSpaceToFilterSpaceTransform(); + if (!userSpaceToFilterSpaceTransform) + return NS_ERROR_OUT_OF_MEMORY; + gfxMatrix m = + nsSVGUtils::ConvertSVGMatrixToThebes(userSpaceToFilterSpaceTransform); + gfxRect r(neededRect.x, neededRect.y, neededRect.width, neededRect.height); + m.Invert(); + r = m.TransformBounds(r); + r.RoundOut(); + nsIntRect dirty; + nsresult rv = nsSVGUtils::GfxRectToIntRect(r, &dirty); + if (NS_FAILED(rv)) + return rv; + mPaintCallback->Paint(&tmpState, mTargetFrame, &dirty, + userSpaceToFilterSpaceTransform); + + gfxContext copyContext(sourceColorAlpha); + copyContext.SetSource(offscreen); + copyContext.Paint(); + } + if (!mSourceColorAlpha.mResultNeededBox.IsEmpty()) { NS_ASSERTION(mSourceColorAlpha.mImageUsers > 0, "Some user must have needed this"); mSourceColorAlpha.mImage.mImage = sourceColorAlpha; @@ -528,3 +545,50 @@ nsSVGFilterInstance::ComputeOutputDirtyRect(nsIntRect* aDirty) *aDirty = result->mResultChangeBox; return NS_OK; } + +nsresult +nsSVGFilterInstance::ComputeSourceNeededRect(nsIntRect* aDirty) +{ + nsresult rv = BuildSources(); + if (NS_FAILED(rv)) + return rv; + + rv = BuildPrimitives(); + if (NS_FAILED(rv)) + return rv; + + if (mPrimitives.IsEmpty()) { + // Nothing should be rendered, so nothing is needed. + return NS_OK; + } + + ComputeResultBoundingBoxes(); + ComputeNeededBoxes(); + aDirty->UnionRect(mSourceColorAlpha.mResultNeededBox, + mSourceAlpha.mResultNeededBox); + return NS_OK; +} + +nsresult +nsSVGFilterInstance::ComputeOutputBBox(nsIntRect* aDirty) +{ + nsresult rv = BuildSources(); + if (NS_FAILED(rv)) + return rv; + + rv = BuildPrimitives(); + if (NS_FAILED(rv)) + return rv; + + if (mPrimitives.IsEmpty()) { + // Nothing should be rendered. + *aDirty = nsIntRect(); + return NS_OK; + } + + ComputeResultBoundingBoxes(); + + PrimitiveInfo* result = &mPrimitives[mPrimitives.Length() - 1]; + *aDirty = result->mResultBoundingBox; + return NS_OK; +} diff --git a/layout/svg/base/src/nsSVGFilterInstance.h b/layout/svg/base/src/nsSVGFilterInstance.h index 35be50d41d42..5798036f1931 100644 --- a/layout/svg/base/src/nsSVGFilterInstance.h +++ b/layout/svg/base/src/nsSVGFilterInstance.h @@ -51,6 +51,8 @@ class nsSVGLength2; class nsSVGElement; +class nsSVGFilterElement; +class nsSVGFilterPaintCallback; /** * This class performs all filter processing. @@ -64,8 +66,9 @@ class NS_STACK_CLASS nsSVGFilterInstance public: float GetPrimitiveLength(nsSVGLength2 *aLength) const; - nsSVGFilterInstance(nsISVGChildFrame *aTargetFrame, - nsIContent* aFilterElement, + nsSVGFilterInstance(nsIFrame *aTargetFrame, + nsSVGFilterPaintCallback *aPaintCallback, + nsSVGFilterElement *aFilterElement, nsIDOMSVGRect *aTargetBBox, const gfxRect& aFilterRect, const nsIntSize& aFilterSpaceSize, @@ -74,6 +77,7 @@ public: const nsIntRect& aDirtyInputRect, PRUint16 aPrimitiveUnits) : mTargetFrame(aTargetFrame), + mPaintCallback(aPaintCallback), mFilterElement(aFilterElement), mTargetBBox(aTargetBBox), mFilterSpaceToDeviceSpaceTransform(aFilterSpaceToDeviceSpaceTransform), @@ -100,6 +104,8 @@ public: nsresult Render(gfxASurface** aOutput); nsresult ComputeOutputDirtyRect(nsIntRect* aDirty); + nsresult ComputeSourceNeededRect(nsIntRect* aDirty); + nsresult ComputeOutputBBox(nsIntRect* aBBox); already_AddRefed GetUserSpaceToFilterSpaceTransform() const; nsIDOMSVGMatrix* GetFilterSpaceToDeviceSpaceTransform() const { @@ -174,15 +180,10 @@ private: aRect->IntersectRect(*aRect, filterSpace); } void ClipToGfxRect(nsIntRect* aRect, const gfxRect& aGfx) const; - nsSVGElement* TargetElement() const - { - nsIFrame* f; - CallQueryInterface(mTargetFrame, &f); - return static_cast(f->GetContent()); - } - nsISVGChildFrame* mTargetFrame; - nsIContent* mFilterElement; + nsIFrame* mTargetFrame; + nsSVGFilterPaintCallback* mPaintCallback; + nsSVGFilterElement* mFilterElement; nsCOMPtr mTargetBBox; nsCOMPtr mFilterSpaceToDeviceSpaceTransform; gfxRect mFilterRect; diff --git a/layout/svg/base/src/nsSVGFilterPaintCallback.h b/layout/svg/base/src/nsSVGFilterPaintCallback.h new file mode 100644 index 000000000000..e8f914a7eae1 --- /dev/null +++ b/layout/svg/base/src/nsSVGFilterPaintCallback.h @@ -0,0 +1,64 @@ +/* -*- 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 Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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_SVGFILTERPAINTCALLBACK_H__ +#define __NS_SVGFILTERPAINTCALLBACK_H__ + +#include "nsRect.h" + +class nsIFrame; +class nsIDOMSVGMatrix; +class nsSVGRenderState; + +class nsSVGFilterPaintCallback { +public: + /** + * Paint the frame contents. aTransform should be applied to aContext + * (either via SetOverrideCTM or by applying the transform to aContext + * directly). + * SVG frames will have had matrix propagation set to false already. + * frames have to do their own thing. + * The caller will do a Save()/Restore() as necessary so feel free + * to mess with context state. + * @param aDirtyRect the dirty rect *in user space pixels* + * @param aTransform the user-space-to-filter-space transform to apply. + * May be null if the identity matrix is requested. + */ + virtual void Paint(nsSVGRenderState *aContext, nsIFrame *aTarget, + const nsIntRect *aDirtyRect, nsIDOMSVGMatrix *aTransform) = 0; +}; + +#endif diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp index c0df04e83fcb..7fc5d445cf6e 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp @@ -97,6 +97,7 @@ nsSVGForeignObjectFrame::Init(nsIContent* aContent, nsIFrame* aPrevInFlow) { nsresult rv = nsSVGForeignObjectFrameBase::Init(aContent, aParent, aPrevInFlow); + AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM); if (NS_SUCCEEDED(rv)) { nsSVGUtils::GetOuterSVGFrame(this)->RegisterForeignObject(this); } @@ -450,10 +451,20 @@ nsSVGForeignObjectFrame::NotifyRedrawUnsuspended() NS_IMETHODIMP nsSVGForeignObjectFrame::SetMatrixPropagation(PRBool aPropagate) { - mPropagateTransform = aPropagate; + if (aPropagate) { + AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM); + } else { + RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM); + } return NS_OK; } +PRBool +nsSVGForeignObjectFrame::GetMatrixPropagation() +{ + return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0; +} + NS_IMETHODIMP nsSVGForeignObjectFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM) { @@ -514,7 +525,7 @@ nsSVGForeignObjectFrame::GetTMIncludingOffset() already_AddRefed nsSVGForeignObjectFrame::GetCanvasTM() { - if (!mPropagateTransform) { + if (!GetMatrixPropagation()) { nsIDOMSVGMatrix *retval; if (mOverrideCTM) { retval = mOverrideCTM; diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.h b/layout/svg/base/src/nsSVGForeignObjectFrame.h index 7970ffc96d19..7845cc9f3bdd 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.h +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.h @@ -114,6 +114,7 @@ public: NS_IMETHOD NotifyRedrawSuspended(); NS_IMETHOD NotifyRedrawUnsuspended(); NS_IMETHOD SetMatrixPropagation(PRBool aPropagate); + virtual PRBool GetMatrixPropagation(); NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM); virtual already_AddRefed GetOverrideCTM(); NS_IMETHOD GetBBox(nsIDOMSVGRect **_retval); diff --git a/layout/svg/base/src/nsSVGGFrame.cpp b/layout/svg/base/src/nsSVGGFrame.cpp index 68bfc6c6718c..02d3a5b225df 100644 --- a/layout/svg/base/src/nsSVGGFrame.cpp +++ b/layout/svg/base/src/nsSVGGFrame.cpp @@ -80,13 +80,6 @@ nsSVGGFrame::NotifySVGChanged(PRUint32 aFlags) nsSVGGFrameBase::NotifySVGChanged(aFlags); } -NS_IMETHODIMP -nsSVGGFrame::SetMatrixPropagation(PRBool aPropagate) -{ - mPropagateTransform = aPropagate; - return NS_OK; -} - NS_IMETHODIMP nsSVGGFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM) { @@ -105,7 +98,7 @@ nsSVGGFrame::GetOverrideCTM() already_AddRefed nsSVGGFrame::GetCanvasTM() { - if (!mPropagateTransform) { + if (!GetMatrixPropagation()) { nsIDOMSVGMatrix *retval; if (mOverrideCTM) { retval = mOverrideCTM; diff --git a/layout/svg/base/src/nsSVGGFrame.h b/layout/svg/base/src/nsSVGGFrame.h index 6f538e34f253..f8b68104f301 100644 --- a/layout/svg/base/src/nsSVGGFrame.h +++ b/layout/svg/base/src/nsSVGGFrame.h @@ -73,7 +73,6 @@ public: // nsISVGChildFrame interface: virtual void NotifySVGChanged(PRUint32 aFlags); - NS_IMETHOD SetMatrixPropagation(PRBool aPropagate); NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM); virtual already_AddRefed GetOverrideCTM(); diff --git a/layout/svg/base/src/nsSVGGeometryFrame.cpp b/layout/svg/base/src/nsSVGGeometryFrame.cpp index 52e38b61aebd..568cc8ca1ca8 100644 --- a/layout/svg/base/src/nsSVGGeometryFrame.cpp +++ b/layout/svg/base/src/nsSVGGeometryFrame.cpp @@ -65,7 +65,8 @@ nsSVGGeometryFrame::Init(nsIContent* aContent, nsIFrame* aParent, nsIFrame* aPrevInFlow) { - AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD); + AddStateBits((aParent->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) | + NS_STATE_SVG_PROPAGATE_TRANSFORM); nsresult rv = nsSVGGeometryFrameBase::Init(aContent, aParent, aPrevInFlow); return rv; } diff --git a/layout/svg/base/src/nsSVGGlyphFrame.cpp b/layout/svg/base/src/nsSVGGlyphFrame.cpp index de3b56db151a..95ec066ab6dc 100644 --- a/layout/svg/base/src/nsSVGGlyphFrame.cpp +++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp @@ -1284,6 +1284,23 @@ nsSVGGlyphFrame::EnsureTextRun(float *aDrawScale, float *aMetricsScale, return PR_TRUE; } +NS_IMETHODIMP +nsSVGGlyphFrame::SetMatrixPropagation(PRBool aPropagate) +{ + if (aPropagate) { + AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM); + } else { + RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM); + } + return NS_OK; +} + +PRBool +nsSVGGlyphFrame::GetMatrixPropagation() +{ + return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0; +} + //---------------------------------------------------------------------- // helper class diff --git a/layout/svg/base/src/nsSVGGlyphFrame.h b/layout/svg/base/src/nsSVGGlyphFrame.h index d9c30ab1350c..44aeaef59568 100644 --- a/layout/svg/base/src/nsSVGGlyphFrame.h +++ b/layout/svg/base/src/nsSVGGlyphFrame.h @@ -125,7 +125,8 @@ public: virtual void NotifySVGChanged(PRUint32 aFlags); NS_IMETHOD NotifyRedrawSuspended(); NS_IMETHOD NotifyRedrawUnsuspended(); - NS_IMETHOD SetMatrixPropagation(PRBool aPropagate) { return NS_OK; } + NS_IMETHOD SetMatrixPropagation(PRBool aPropagate); + virtual PRBool GetMatrixPropagation(); NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM) { return NS_ERROR_FAILURE; } virtual already_AddRefed GetOverrideCTM() { return nsnull; } NS_IMETHOD_(PRBool) IsDisplayContainer() { return PR_FALSE; } diff --git a/layout/svg/base/src/nsSVGInnerSVGFrame.cpp b/layout/svg/base/src/nsSVGInnerSVGFrame.cpp index e9585f6837c4..21c1e18f6673 100644 --- a/layout/svg/base/src/nsSVGInnerSVGFrame.cpp +++ b/layout/svg/base/src/nsSVGInnerSVGFrame.cpp @@ -85,7 +85,6 @@ public: // nsISVGChildFrame interface: NS_IMETHOD PaintSVG(nsSVGRenderState *aContext, nsIntRect *aDirtyRect); virtual void NotifySVGChanged(PRUint32 aFlags); - NS_IMETHOD SetMatrixPropagation(PRBool aPropagate); NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM); virtual already_AddRefed GetOverrideCTM(); NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint); @@ -228,13 +227,6 @@ nsSVGInnerSVGFrame::NotifySVGChanged(PRUint32 aFlags) nsSVGInnerSVGFrameBase::NotifySVGChanged(aFlags); } -NS_IMETHODIMP -nsSVGInnerSVGFrame::SetMatrixPropagation(PRBool aPropagate) -{ - mPropagateTransform = aPropagate; - return NS_OK; -} - NS_IMETHODIMP nsSVGInnerSVGFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM) { diff --git a/layout/svg/base/src/nsSVGIntegrationUtils.cpp b/layout/svg/base/src/nsSVGIntegrationUtils.cpp new file mode 100644 index 000000000000..1f454d5f4367 --- /dev/null +++ b/layout/svg/base/src/nsSVGIntegrationUtils.cpp @@ -0,0 +1,414 @@ +/* -*- 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 IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * rocallahan@mozilla.com + * + * 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 ***** */ + +#include "nsSVGIntegrationUtils.h" + +#include "nsSVGUtils.h" +#include "nsSVGEffects.h" +#include "nsRegion.h" +#include "nsLayoutUtils.h" +#include "nsDisplayList.h" +#include "nsSVGMatrix.h" +#include "nsSVGFilterPaintCallback.h" + +// ---------------------------------------------------------------------- + +PRBool +nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame) +{ + const nsStyleSVGReset *style = aFrame->GetStyleSVGReset(); + return style->mFilter || style->mClipPath || style->mMask; +} + +// Get the union the frame border-box rects over all continuations, +// relative to aFirst. This defines "user space" for non-SVG frames. +static nsRect GetNonSVGUserSpace(nsIFrame* aFirst) +{ + NS_ASSERTION(!aFirst->GetPrevContinuation(), "Not first continuation"); + return nsLayoutUtils::GetAllInFlowRectsUnion(aFirst, aFirst); +} + +static nsRect +GetPreEffectsOverflowRect(nsIFrame* aFrame) +{ + nsRect* r = static_cast(aFrame->GetProperty(nsGkAtoms::preEffectsBBoxProperty)); + if (r) + return *r; + return aFrame->GetOverflowRect(); +} + +struct BBoxCollector : public nsLayoutUtils::BoxCallback { + nsIFrame* mReferenceFrame; + nsIFrame* mCurrentFrame; + const nsRect& mCurrentFrameOverflowArea; + nsRect mResult; + + BBoxCollector(nsIFrame* aReferenceFrame, nsIFrame* aCurrentFrame, + const nsRect& aCurrentFrameOverflowArea) + : mReferenceFrame(aReferenceFrame), mCurrentFrame(aCurrentFrame), + mCurrentFrameOverflowArea(aCurrentFrameOverflowArea) {} + + virtual void AddBox(nsIFrame* aFrame) { + nsRect overflow = aFrame == mCurrentFrame ? mCurrentFrameOverflowArea + : GetPreEffectsOverflowRect(aFrame); + mResult.UnionRect(mResult, overflow + aFrame->GetOffsetTo(mReferenceFrame)); + } +}; + +static nsRect +GetSVGBBox(nsIFrame* aNonSVGFrame, nsIFrame* aCurrentFrame, + const nsRect& aCurrentOverflow, const nsRect& aUserSpaceRect) +{ + NS_ASSERTION(!aNonSVGFrame->GetPrevContinuation(), + "Need first continuation here"); + // Compute union of all overflow areas relative to 'first'. + BBoxCollector collector(aNonSVGFrame, aCurrentFrame, aCurrentOverflow); + nsLayoutUtils::GetAllInFlowBoxes(aNonSVGFrame, &collector); + // Get it into "user space" for non-SVG frames + return collector.mResult - aUserSpaceRect.TopLeft(); +} + +nsRect +nsSVGIntegrationUtils::ComputeFrameEffectsRect(nsIFrame* aFrame, + const nsRect& aOverflowRect) +{ + PRBool isOK; + nsIFrame* firstFrame = + nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame); + nsSVGEffects::EffectProperties effectProperties = + nsSVGEffects::GetEffectProperties(firstFrame); + nsSVGFilterFrame *filterFrame = effectProperties.mFilter ? + effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull; + if (!filterFrame) + return aOverflowRect; + + // XXX this isn't really right. We can't compute the correct filter + // bbox until all aFrame's continuations have been reflowed. + // but then it's too late to set the overflow areas for the earlier frames. + nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame); + nsRect r = GetSVGBBox(firstFrame, aFrame, aOverflowRect, userSpaceRect); + // r is relative to user space + PRUint32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel(); + r.ScaleRoundOutInverse(appUnitsPerDevPixel); + r = filterFrame->GetFilterBBox(firstFrame, &r); + r.ScaleRoundOut(appUnitsPerDevPixel); + // Make it relative to aFrame again + return r + userSpaceRect.TopLeft() - aFrame->GetOffsetTo(firstFrame); +} + +nsRect +nsSVGIntegrationUtils::GetInvalidAreaForChangedSource(nsIFrame* aFrame, + const nsRect& aInvalidRect) +{ + // Don't bother calling GetEffectProperties; the filter property should + // already have been set up during reflow/ComputeFrameEffectsRect + nsIFrame* firstFrame = + nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame); + nsSVGFilterFrame* filterFrame = nsSVGEffects::GetFilterFrame(firstFrame); + if (!filterFrame) + return aInvalidRect; + + PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel(); + nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame); + nsPoint offset = aFrame->GetOffsetTo(firstFrame) - userSpaceRect.TopLeft(); + nsRect r = aInvalidRect + offset; + r.ScaleRoundOutInverse(appUnitsPerDevPixel); + r = filterFrame->GetInvalidationBBox(firstFrame, r); + r.ScaleRoundOut(appUnitsPerDevPixel); + return r - offset; +} + +nsRect +nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame, + const nsRect& aDamageRect) +{ + // Don't bother calling GetEffectProperties; the filter property should + // already have been set up during reflow/ComputeFrameEffectsRect + nsIFrame* firstFrame = + nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame); + nsSVGFilterFrame* filterFrame = + nsSVGEffects::GetFilterFrame(firstFrame); + if (!filterFrame) + return aDamageRect; + + PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel(); + nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame); + nsPoint offset = aFrame->GetOffsetTo(firstFrame) - userSpaceRect.TopLeft(); + nsRect r = aDamageRect + offset; + r.ScaleRoundOutInverse(appUnitsPerDevPixel); + r = filterFrame->GetSourceForInvalidArea(firstFrame, r); + r.ScaleRoundOut(appUnitsPerDevPixel); + return r - offset; +} + +PRBool +nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt) +{ + nsIFrame* firstFrame = + nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame); + nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame); + // get point relative to userSpaceRect + nsPoint pt = aPt + aFrame->GetOffsetTo(firstFrame) - userSpaceRect.TopLeft(); + return nsSVGUtils::HitTestClip(firstFrame, pt); +} + +class RegularFramePaintCallback : public nsSVGFilterPaintCallback +{ +public: + RegularFramePaintCallback(nsDisplayListBuilder* aBuilder, + nsDisplayList* aInnerList, + const nsPoint& aOffset) + : mBuilder(aBuilder), mInnerList(aInnerList), mOffset(aOffset) {} + + virtual void Paint(nsSVGRenderState *aContext, nsIFrame *aTarget, + const nsIntRect* aDirtyRect, nsIDOMSVGMatrix *aTransform) + { + nsIRenderingContext* ctx = aContext->GetRenderingContext(aTarget); + gfxContext* gfxCtx = aContext->GetGfxContext(); + + if (aTransform) { + // Transform by aTransform first + gfxMatrix m = nsSVGUtils::ConvertSVGMatrixToThebes(aTransform); + gfxCtx->Multiply(m); + } + + // We're expected to paint with 1 unit equal to 1 CSS pixel. But + // mInnerList->Paint expects 1 unit to equal 1 device pixel. So + // adjust. + gfxFloat scale = + nsPresContext::AppUnitsToFloatCSSPixels(aTarget->PresContext()->AppUnitsPerDevPixel()); + gfxCtx->Scale(scale, scale); + + nsIRenderingContext::AutoPushTranslation push(ctx, -mOffset.x, -mOffset.y); + nsRect dirty; + if (aDirtyRect) { + dirty = *aDirtyRect; + dirty.ScaleRoundOut(nsIDeviceContext::AppUnitsPerCSSPixel()); + dirty += mOffset; + } else { + dirty = mInnerList->GetBounds(mBuilder); + } + mInnerList->Paint(mBuilder, ctx, dirty); + } + +private: + nsDisplayListBuilder* mBuilder; + nsDisplayList* mInnerList; + nsPoint mOffset; +}; + +void +nsSVGIntegrationUtils::PaintFramesWithEffects(nsIRenderingContext* aCtx, + nsIFrame* aEffectsFrame, + const nsRect& aDirtyRect, + nsDisplayListBuilder* aBuilder, + nsDisplayList* aInnerList) +{ +#ifdef DEBUG + nsISVGChildFrame *svgChildFrame; + CallQueryInterface(aEffectsFrame, &svgChildFrame); +#endif + NS_ASSERTION(!svgChildFrame, "Should never be called on an SVG frame"); + + float opacity = aEffectsFrame->GetStyleDisplay()->mOpacity; + if (opacity == 0.0f) + return; + + /* Properties are added lazily and may have been removed by a restyle, + so make sure all applicable ones are set again. */ + nsIFrame* firstFrame = + nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aEffectsFrame); + nsSVGEffects::EffectProperties effectProperties = + nsSVGEffects::GetEffectProperties(firstFrame); + + /* SVG defines the following rendering model: + * + * 1. Render geometry + * 2. Apply filter + * 3. Apply clipping, masking, group opacity + * + * We follow this, but perform a couple of optimizations: + * + * + Use cairo's clipPath when representable natively (single object + * clip region). + * + * + Merge opacity and masking if both used together. + */ + + PRBool isOK = PR_TRUE; + nsSVGClipPathFrame *clipPathFrame = effectProperties.mClipPath ? + effectProperties.mClipPath->GetClipPathFrame(&isOK) : nsnull; + nsSVGFilterFrame *filterFrame = effectProperties.mFilter ? + effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull; + nsSVGMaskFrame *maskFrame = effectProperties.mMask ? + effectProperties.mMask->GetMaskFrame(&isOK) : nsnull; + + PRBool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : PR_TRUE; + + if (!isOK) { + // Some resource is missing. We shouldn't paint anything. + return; + } + + gfxContext* gfx = aCtx->ThebesContext(); + gfxMatrix savedCTM = gfx->CurrentMatrix(); + nsSVGRenderState svgContext(aCtx); + + nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame) + aBuilder->ToReferenceFrame(firstFrame); + PRInt32 appUnitsPerDevPixel = aEffectsFrame->PresContext()->AppUnitsPerDevPixel(); + userSpaceRect.ScaleRoundPreservingCentersInverse(appUnitsPerDevPixel); + userSpaceRect.ScaleRoundOut(appUnitsPerDevPixel); + aCtx->Translate(userSpaceRect.x, userSpaceRect.y); + + nsCOMPtr matrix = GetInitialMatrix(aEffectsFrame); + + PRBool complexEffects = PR_FALSE; + /* Check if we need to do additional operations on this child's + * rendering, which necessitates rendering into another surface. */ + if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) { + complexEffects = PR_TRUE; + gfx->Save(); + gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA); + } + + /* If this frame has only a trivial clipPath, set up cairo's clipping now so + * we can just do normal painting and get it clipped appropriately. + */ + if (clipPathFrame && isTrivialClip) { + gfx->Save(); + clipPathFrame->ClipPaint(&svgContext, aEffectsFrame, matrix); + } + + /* Paint the child */ + if (filterFrame) { + RegularFramePaintCallback paint(aBuilder, aInnerList, userSpaceRect.TopLeft()); + nsRect r = aDirtyRect - userSpaceRect.TopLeft(); + r.ScaleRoundOutInverse(appUnitsPerDevPixel); + filterFrame->FilterPaint(&svgContext, aEffectsFrame, &paint, &r); + } else { + gfx->SetMatrix(savedCTM); + aInnerList->Paint(aBuilder, aCtx, aDirtyRect); + aCtx->Translate(userSpaceRect.x, userSpaceRect.y); + } + + if (clipPathFrame && isTrivialClip) { + gfx->Restore(); + } + + /* No more effects, we're done. */ + if (!complexEffects) { + gfx->SetMatrix(savedCTM); + return; + } + + gfx->PopGroupToSource(); + + nsRefPtr maskSurface = + maskFrame ? maskFrame->ComputeMaskAlpha(&svgContext, aEffectsFrame, + matrix, opacity) : nsnull; + + nsRefPtr clipMaskSurface; + if (clipPathFrame && !isTrivialClip) { + gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA); + + nsresult rv = clipPathFrame->ClipPaint(&svgContext, aEffectsFrame, matrix); + clipMaskSurface = gfx->PopGroup(); + + if (NS_SUCCEEDED(rv) && clipMaskSurface) { + // Still more set after clipping, so clip to another surface + if (maskSurface || opacity != 1.0f) { + gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA); + gfx->Mask(clipMaskSurface); + gfx->PopGroupToSource(); + } else { + gfx->Mask(clipMaskSurface); + } + } + } + + if (maskSurface) { + gfx->Mask(maskSurface); + } else if (opacity != 1.0f) { + gfx->Paint(opacity); + } + + gfx->Restore(); + gfx->SetMatrix(savedCTM); +} + +already_AddRefed +nsSVGIntegrationUtils::GetInitialMatrix(nsIFrame* aNonSVGFrame) +{ + NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG), + "SVG frames should not get here"); + PRInt32 appUnitsPerDevPixel = aNonSVGFrame->PresContext()->AppUnitsPerDevPixel(); + nsCOMPtr matrix; + float devPxPerCSSPx = + 1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel); + NS_NewSVGMatrix(getter_AddRefs(matrix), + devPxPerCSSPx, 0.0f, + 0.0f, devPxPerCSSPx); + return matrix.forget(); +} + +gfxRect +nsSVGIntegrationUtils::GetSVGRectForNonSVGFrame(nsIFrame* aNonSVGFrame) +{ + NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG), + "SVG frames should not get here"); + nsIFrame* firstFrame = + nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aNonSVGFrame); + nsRect r = GetNonSVGUserSpace(firstFrame); + nsPresContext* presContext = firstFrame->PresContext(); + return gfxRect(0, 0, presContext->AppUnitsToFloatCSSPixels(r.width), + presContext->AppUnitsToFloatCSSPixels(r.height)); +} + +gfxRect +nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame) +{ + NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG), + "SVG frames should not get here"); + nsIFrame* firstFrame = + nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aNonSVGFrame); + nsRect userSpaceRect = GetNonSVGUserSpace(firstFrame); + nsRect r = GetSVGBBox(firstFrame, nsnull, nsRect(), userSpaceRect); + gfxRect result(r.x, r.y, r.width, r.height); + nsPresContext* presContext = aNonSVGFrame->PresContext(); + result.ScaleInverse(presContext->AppUnitsPerCSSPixel()); + return result; +} diff --git a/layout/svg/base/src/nsSVGIntegrationUtils.h b/layout/svg/base/src/nsSVGIntegrationUtils.h new file mode 100644 index 000000000000..2a6c19a973bc --- /dev/null +++ b/layout/svg/base/src/nsSVGIntegrationUtils.h @@ -0,0 +1,116 @@ +/* -*- 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 IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * rocallahan@mozilla.com + * + * 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 NSSVGINTEGRATIONUTILS_H_ +#define NSSVGINTEGRATIONUTILS_H_ + +#include "nsPoint.h" +#include "nsRect.h" +#include "gfxRect.h" + +class nsIFrame; +class nsDisplayListBuilder; +class nsDisplayList; +class nsIRenderingContext; +class nsIDOMSVGMatrix; + +/***** Integration of SVG effects with regular frame painting *****/ + +class nsSVGIntegrationUtils +{ +public: + static PRBool + UsingEffectsForFrame(const nsIFrame* aFrame); + + /** + * Adjust overflow rect for effects. + * XXX this is a problem. We really need to compute the effects rect for + * a whole chain of frames for a given element at once. but we have no + * way to do this effectively with Gecko's current reflow architecture. + * See http://groups.google.com/group/mozilla.dev.tech.layout/msg/6b179066f3051f65 + */ + static nsRect + ComputeFrameEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect); + /** + * Adjust the frame's invalidation area to cover effects + */ + static nsRect + GetInvalidAreaForChangedSource(nsIFrame* aFrame, const nsRect& aInvalidRect); + /** + * Figure out which area of the source is needed given an area to + * repaint + */ + static nsRect + GetRequiredSourceForInvalidArea(nsIFrame* aFrame, const nsRect& aDamageRect); + /** + * Returns true if the given point is not clipped out by effects. + * @param aPt in appunits relative to aFrame + */ + static PRBool + HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt); + + /** + * Paint non-SVG frame with SVG effects. + * @param aOffset the offset in appunits where aFrame should be positioned + * in aCtx's coordinate system + */ + static void + PaintFramesWithEffects(nsIRenderingContext* aCtx, + nsIFrame* aEffectsFrame, const nsRect& aDirtyRect, + nsDisplayListBuilder* aBuilder, + nsDisplayList* aInnerList); + + static already_AddRefed + GetInitialMatrix(nsIFrame* aNonSVGFrame); + /** + * Returns aNonSVGFrame's rect in CSS pixel units. This is the union + * of all its continuations' rectangles. The top-left is always 0,0 + * since "user space" origin for non-SVG frames is the top-left of the + * union of all the continuations' rectangles. + */ + static gfxRect + GetSVGRectForNonSVGFrame(nsIFrame* aNonSVGFrame); + /** + * Returns aNonSVGFrame's bounding box in CSS units. This is the union + * of all its continuations' overflow areas, relative to the top-left + * of all the continuations' rectangles. + */ + static gfxRect + GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame); +}; + +#endif /*NSSVGINTEGRATIONUTILS_H_*/ diff --git a/layout/svg/base/src/nsSVGMaskFrame.cpp b/layout/svg/base/src/nsSVGMaskFrame.cpp index f6977008dcbf..1089a4f6938d 100644 --- a/layout/svg/base/src/nsSVGMaskFrame.cpp +++ b/layout/svg/base/src/nsSVGMaskFrame.cpp @@ -61,7 +61,7 @@ NS_NewSVGMaskFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContex already_AddRefed nsSVGMaskFrame::ComputeMaskAlpha(nsSVGRenderState *aContext, - nsISVGChildFrame* aParent, + nsIFrame* aParent, nsIDOMSVGMatrix* aMatrix, float aOpacity) { @@ -79,67 +79,23 @@ nsSVGMaskFrame::ComputeMaskAlpha(nsSVGRenderState *aContext, gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA); { - nsIFrame *frame; - CallQueryInterface(aParent, &frame); - nsSVGElement *parent = static_cast(frame->GetContent()); - - float x, y, width, height; - nsSVGMaskElement *mask = static_cast(mContent); - nsSVGLength2 *tmpX, *tmpY, *tmpWidth, *tmpHeight; - tmpX = &mask->mLengthAttributes[nsSVGMaskElement::X]; - tmpY = &mask->mLengthAttributes[nsSVGMaskElement::Y]; - tmpWidth = &mask->mLengthAttributes[nsSVGMaskElement::WIDTH]; - tmpHeight = &mask->mLengthAttributes[nsSVGMaskElement::HEIGHT]; - PRUint16 units = mask->mEnumAttributes[nsSVGMaskElement::MASKUNITS].GetAnimValue(); - + nsCOMPtr bbox; if (units == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { - - aParent->SetMatrixPropagation(PR_FALSE); - aParent->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION | - nsISVGChildFrame::TRANSFORM_CHANGED); - - nsCOMPtr bbox; - aParent->GetBBox(getter_AddRefs(bbox)); - - aParent->SetMatrixPropagation(PR_TRUE); - aParent->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION | - nsISVGChildFrame::TRANSFORM_CHANGED); - + bbox = nsSVGUtils::GetBBox(aParent); if (!bbox) return nsnull; - -#ifdef DEBUG_tor - bbox->GetX(&x); - bbox->GetY(&y); - bbox->GetWidth(&width); - bbox->GetHeight(&height); - - fprintf(stderr, "mask bbox: %f,%f %fx%f\n", x, y, width, height); -#endif - - bbox->GetX(&x); - x += nsSVGUtils::ObjectSpace(bbox, tmpX); - bbox->GetY(&y); - y += nsSVGUtils::ObjectSpace(bbox, tmpY); - width = nsSVGUtils::ObjectSpace(bbox, tmpWidth); - height = nsSVGUtils::ObjectSpace(bbox, tmpHeight); - } else { - x = nsSVGUtils::UserSpace(parent, tmpX); - y = nsSVGUtils::UserSpace(parent, tmpY); - width = nsSVGUtils::UserSpace(parent, tmpWidth); - height = nsSVGUtils::UserSpace(parent, tmpHeight); } -#ifdef DEBUG_tor - fprintf(stderr, "mask clip: %f,%f %fx%f\n", x, y, width, height); -#endif + gfxRect maskArea = nsSVGUtils::GetRelativeRect(units, + &mask->mLengthAttributes[nsSVGMaskElement::X], bbox, aParent); gfx->Save(); - nsSVGUtils::SetClipRect(gfx, aMatrix, x, y, width, height); + nsSVGUtils::SetClipRect(gfx, aMatrix, maskArea.X(), maskArea.Y(), + maskArea.Width(), maskArea.Height()); } mMaskParent = aParent; diff --git a/layout/svg/base/src/nsSVGMaskFrame.h b/layout/svg/base/src/nsSVGMaskFrame.h index cd464c0fd990..6b1202080ccb 100644 --- a/layout/svg/base/src/nsSVGMaskFrame.h +++ b/layout/svg/base/src/nsSVGMaskFrame.h @@ -57,7 +57,7 @@ protected: public: // nsSVGMaskFrame method: already_AddRefed ComputeMaskAlpha(nsSVGRenderState *aContext, - nsISVGChildFrame* aParent, + nsIFrame* aParent, nsIDOMSVGMatrix* aMatrix, float aOpacity = 1.0f); @@ -95,7 +95,7 @@ private: nsSVGMaskFrame *mFrame; }; - nsISVGChildFrame *mMaskParent; + nsIFrame *mMaskParent; nsCOMPtr mMaskParentMatrix; // recursion prevention flag PRPackedBool mInUse; diff --git a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp index a32b087667fc..0c5ffd7ec830 100644 --- a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp +++ b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp @@ -499,10 +499,20 @@ nsSVGPathGeometryFrame::NotifyRedrawUnsuspended() NS_IMETHODIMP nsSVGPathGeometryFrame::SetMatrixPropagation(PRBool aPropagate) { - mPropagateTransform = aPropagate; + if (aPropagate) { + AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM); + } else { + RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM); + } return NS_OK; } +PRBool +nsSVGPathGeometryFrame::GetMatrixPropagation() +{ + return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0; +} + NS_IMETHODIMP nsSVGPathGeometryFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM) { @@ -538,7 +548,7 @@ nsSVGPathGeometryFrame::GetCanvasTM(nsIDOMSVGMatrix * *aCTM) { *aCTM = nsnull; - if (!mPropagateTransform) { + if (!GetMatrixPropagation()) { if (mOverrideCTM) { *aCTM = mOverrideCTM; NS_ADDREF(*aCTM); diff --git a/layout/svg/base/src/nsSVGPathGeometryFrame.h b/layout/svg/base/src/nsSVGPathGeometryFrame.h index d28390ffc594..4d95ee8e542d 100644 --- a/layout/svg/base/src/nsSVGPathGeometryFrame.h +++ b/layout/svg/base/src/nsSVGPathGeometryFrame.h @@ -65,8 +65,7 @@ class nsSVGPathGeometryFrame : public nsSVGPathGeometryFrameBase, nsStyleContext* aContext); protected: nsSVGPathGeometryFrame(nsStyleContext* aContext) : - nsSVGPathGeometryFrameBase(aContext), - mPropagateTransform(PR_TRUE) {} + nsSVGPathGeometryFrameBase(aContext) {} public: // nsISupports interface: @@ -112,6 +111,7 @@ protected: NS_IMETHOD NotifyRedrawSuspended(); NS_IMETHOD NotifyRedrawUnsuspended(); NS_IMETHOD SetMatrixPropagation(PRBool aPropagate); + virtual PRBool GetMatrixPropagation(); NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM); virtual already_AddRefed GetOverrideCTM(); NS_IMETHOD GetBBox(nsIDOMSVGRect **_retval); @@ -143,7 +143,6 @@ private: void RemovePathProperties(); nsCOMPtr mOverrideCTM; - PRPackedBool mPropagateTransform; }; #endif // __NS_SVGPATHGEOMETRYFRAME_H__ diff --git a/layout/svg/base/src/nsSVGTSpanFrame.cpp b/layout/svg/base/src/nsSVGTSpanFrame.cpp index a1ecdea6737a..9e398f28fcf0 100644 --- a/layout/svg/base/src/nsSVGTSpanFrame.cpp +++ b/layout/svg/base/src/nsSVGTSpanFrame.cpp @@ -102,13 +102,6 @@ nsSVGTSpanFrame::AttributeChanged(PRInt32 aNameSpaceID, //---------------------------------------------------------------------- // nsISVGChildFrame methods -NS_IMETHODIMP -nsSVGTSpanFrame::SetMatrixPropagation(PRBool aPropagate) -{ - mPropagateTransform = aPropagate; - return NS_OK; -} - NS_IMETHODIMP nsSVGTSpanFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM) { diff --git a/layout/svg/base/src/nsSVGTSpanFrame.h b/layout/svg/base/src/nsSVGTSpanFrame.h index 95a9bfa89a5e..e394427bda2e 100644 --- a/layout/svg/base/src/nsSVGTSpanFrame.h +++ b/layout/svg/base/src/nsSVGTSpanFrame.h @@ -81,7 +81,6 @@ public: } #endif // nsISVGChildFrame interface: - NS_IMETHOD SetMatrixPropagation(PRBool aPropagate); NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM); virtual already_AddRefed GetOverrideCTM(); diff --git a/layout/svg/base/src/nsSVGTextFrame.cpp b/layout/svg/base/src/nsSVGTextFrame.cpp index 0dffe46cb37c..44da352c0714 100644 --- a/layout/svg/base/src/nsSVGTextFrame.cpp +++ b/layout/svg/base/src/nsSVGTextFrame.cpp @@ -212,13 +212,6 @@ nsSVGTextFrame::NotifyRedrawUnsuspended() return nsSVGTextFrameBase::NotifyRedrawUnsuspended(); } -NS_IMETHODIMP -nsSVGTextFrame::SetMatrixPropagation(PRBool aPropagate) -{ - mPropagateTransform = aPropagate; - return NS_OK; -} - NS_IMETHODIMP nsSVGTextFrame::SetOverrideCTM(nsIDOMSVGMatrix *aCTM) { @@ -282,7 +275,7 @@ nsSVGTextFrame::GetBBox(nsIDOMSVGRect **_retval) already_AddRefed nsSVGTextFrame::GetCanvasTM() { - if (!mPropagateTransform) { + if (!GetMatrixPropagation()) { nsIDOMSVGMatrix *retval; if (mOverrideCTM) { retval = mOverrideCTM; diff --git a/layout/svg/base/src/nsSVGTextFrame.h b/layout/svg/base/src/nsSVGTextFrame.h index 39ae2afc6d4f..48b3e4dbcc6e 100644 --- a/layout/svg/base/src/nsSVGTextFrame.h +++ b/layout/svg/base/src/nsSVGTextFrame.h @@ -51,7 +51,6 @@ protected: nsSVGTextFrame(nsStyleContext* aContext) : nsSVGTextFrameBase(aContext), mMetricsState(unsuspended), - mPropagateTransform(PR_TRUE), mPositioningDirty(PR_TRUE) {} public: @@ -75,7 +74,6 @@ public: #endif // nsISVGChildFrame interface: - NS_IMETHOD SetMatrixPropagation(PRBool aPropagate); NS_IMETHOD SetOverrideCTM(nsIDOMSVGMatrix *aCTM); virtual already_AddRefed GetOverrideCTM(); virtual void NotifySVGChanged(PRUint32 aFlags); @@ -119,7 +117,6 @@ private: enum UpdateState { unsuspended, suspended }; UpdateState mMetricsState; - PRPackedBool mPropagateTransform; PRPackedBool mPositioningDirty; }; diff --git a/layout/svg/base/src/nsSVGUtils.cpp b/layout/svg/base/src/nsSVGUtils.cpp index aa9eb47ddbe9..90f955679608 100644 --- a/layout/svg/base/src/nsSVGUtils.cpp +++ b/layout/svg/base/src/nsSVGUtils.cpp @@ -83,6 +83,8 @@ #include "nsIDOMSVGUnitTypes.h" #include "nsSVGRect.h" #include "nsSVGEffects.h" +#include "nsSVGIntegrationUtils.h" +#include "nsSVGFilterPaintCallback.h" gfxASurface *nsSVGUtils::mThebesComputationalSurface = nsnull; @@ -217,8 +219,14 @@ nsSVGUtils::GetFontSize(nsIContent *aContent) return 1.0f; } - return nsPresContext::AppUnitsToFloatCSSPixels(frame->GetStyleFont()->mSize) / - frame->PresContext()->TextZoom(); + return GetFontSize(frame); +} + +float +nsSVGUtils::GetFontSize(nsIFrame *aFrame) +{ + return nsPresContext::AppUnitsToFloatCSSPixels(aFrame->GetStyleFont()->mSize) / + aFrame->PresContext()->TextZoom(); } float @@ -230,8 +238,14 @@ nsSVGUtils::GetFontXHeight(nsIContent *aContent) return 1.0f; } + return GetFontXHeight(frame); +} + +float +nsSVGUtils::GetFontXHeight(nsIFrame *aFrame) +{ nsCOMPtr fontMetrics; - nsLayoutUtils::GetFontMetricsForFrame(frame, getter_AddRefs(fontMetrics)); + nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fontMetrics)); if (!fontMetrics) { NS_WARNING("no FontMetrics in GetFontXHeight()"); @@ -241,7 +255,7 @@ nsSVGUtils::GetFontXHeight(nsIContent *aContent) nscoord xHeight; fontMetrics->GetXHeight(xHeight); return nsPresContext::AppUnitsToFloatCSSPixels(xHeight) / - frame->PresContext()->TextZoom(); + aFrame->PresContext()->TextZoom(); } void @@ -580,7 +594,7 @@ nsSVGUtils::FindFilterInvalidation(nsIFrame *aFrame, const nsRect& aRect) if (property) { nsSVGFilterFrame *filter = property->GetFilterFrame(nsnull); if (filter) { - rect = filter->GetInvalidationRegion(aFrame, rect); + rect = filter->GetInvalidationBBox(aFrame, rect); } } aFrame = aFrame->GetParent(); @@ -655,7 +669,7 @@ nsSVGUtils::ComputeNormalizedHypotenuse(double aWidth, double aHeight) } float -nsSVGUtils::ObjectSpace(nsIDOMSVGRect *aRect, nsSVGLength2 *aLength) +nsSVGUtils::ObjectSpace(nsIDOMSVGRect *aRect, const nsSVGLength2 *aLength) { float fraction, axis; @@ -685,11 +699,17 @@ nsSVGUtils::ObjectSpace(nsIDOMSVGRect *aRect, nsSVGLength2 *aLength) } float -nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, nsSVGLength2 *aLength) +nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength) { return aLength->GetAnimValue(aSVGElement); } +float +nsSVGUtils::UserSpace(nsIFrame *aNonSVGContext, const nsSVGLength2 *aLength) +{ + return aLength->GetAnimValue(aNonSVGContext); +} + void nsSVGUtils::TransformPoint(nsIDOMSVGMatrix *matrix, float *x, float *y) @@ -851,6 +871,9 @@ nsSVGUtils::GetViewBoxTransform(float aViewportWidth, float aViewportHeight, already_AddRefed nsSVGUtils::GetCanvasTM(nsIFrame *aFrame) { + if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) + return nsSVGIntegrationUtils::GetInitialMatrix(aFrame); + if (!aFrame->IsLeaf()) { // foreignObject is the one non-leaf svg frame that isn't a SVGContainer if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) { @@ -917,6 +940,26 @@ nsSVGUtils::RemoveObserver(nsISupports *aObserver, nsISupports *aTarget) // ************************************************************ +class SVGPaintCallback : public nsSVGFilterPaintCallback +{ +public: + virtual void Paint(nsSVGRenderState *aContext, nsIFrame *aTarget, + const nsIntRect* aDirtyRect, nsIDOMSVGMatrix *aTransform) + { + nsISVGChildFrame *svgChildFrame; + CallQueryInterface(aTarget, &svgChildFrame); + NS_ASSERTION(svgChildFrame, "Expected SVG frame here"); + + if (aTransform) { + svgChildFrame->SetOverrideCTM(aTransform); + svgChildFrame->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION | + nsISVGChildFrame::TRANSFORM_CHANGED); + } + + svgChildFrame->PaintSVG(aContext, const_cast(aDirtyRect)); + } +}; + void nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext, nsIntRect *aDirtyRect, @@ -938,14 +981,21 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext, nsSVGEffects::EffectProperties effectProperties = nsSVGEffects::GetEffectProperties(aFrame); - /* Check if we need to draw anything */ + PRBool isOK = PR_TRUE; + nsSVGFilterFrame *filterFrame = effectProperties.mFilter ? + effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull; + + /* Check if we need to draw anything. HasValidCoveredRect only returns + * true for path geometry and glyphs, so basically we're traversing + * all containers and we can only skip leaves here. + */ if (aDirtyRect && svgChildFrame->HasValidCoveredRect()) { - nsRect rect = *aDirtyRect; - rect.ScaleRoundOut(aFrame->PresContext()->AppUnitsPerDevPixel()); - if (effectProperties.mFilter) { - if (!rect.Intersects(FindFilterInvalidation(aFrame, aFrame->GetRect()))) + if (filterFrame) { + if (!aDirtyRect->Intersects(filterFrame->GetFilterBBox(aFrame, nsnull))) return; } else { + nsRect rect = *aDirtyRect; + rect.ScaleRoundOut(aFrame->PresContext()->AppUnitsPerDevPixel()); if (!rect.Intersects(aFrame->GetRect())) return; } @@ -971,11 +1021,8 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext, gfxContext *gfx = aContext->GetGfxContext(); PRBool complexEffects = PR_FALSE; - PRBool isOK = PR_TRUE; nsSVGClipPathFrame *clipPathFrame = effectProperties.mClipPath ? effectProperties.mClipPath->GetClipPathFrame(&isOK) : nsnull; - nsSVGFilterFrame *filterFrame = effectProperties.mFilter ? - effectProperties.mFilter->GetFilterFrame(&isOK) : nsnull; nsSVGMaskFrame *maskFrame = effectProperties.mMask ? effectProperties.mMask->GetMaskFrame(&isOK) : nsnull; @@ -1002,12 +1049,13 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext, */ if (clipPathFrame && isTrivialClip) { gfx->Save(); - clipPathFrame->ClipPaint(aContext, svgChildFrame, matrix); + clipPathFrame->ClipPaint(aContext, aFrame, matrix); } /* Paint the child */ if (filterFrame) { - filterFrame->FilterPaint(aContext, svgChildFrame, aDirtyRect); + SVGPaintCallback paintCallback; + filterFrame->FilterPaint(aContext, aFrame, &paintCallback, aDirtyRect); } else { svgChildFrame->PaintSVG(aContext, aDirtyRect); } @@ -1023,14 +1071,14 @@ nsSVGUtils::PaintChildWithEffects(nsSVGRenderState *aContext, gfx->PopGroupToSource(); nsRefPtr maskSurface = - maskFrame ? maskFrame->ComputeMaskAlpha(aContext, svgChildFrame, + maskFrame ? maskFrame->ComputeMaskAlpha(aContext, aFrame, matrix, opacity) : nsnull; nsRefPtr clipMaskSurface; if (clipPathFrame && !isTrivialClip) { gfx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA); - nsresult rv = clipPathFrame->ClipPaint(aContext, svgChildFrame, matrix); + nsresult rv = clipPathFrame->ClipPaint(aContext, aFrame, matrix); clipMaskSurface = gfx->PopGroup(); if (NS_SUCCEEDED(rv) && clipMaskSurface) { @@ -1077,11 +1125,8 @@ nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint) return PR_FALSE; } - nsISVGChildFrame* SVGFrame; - CallQueryInterface(aFrame, &SVGFrame); - nsCOMPtr matrix = GetCanvasTM(aFrame); - return clipPathFrame->ClipHitTest(SVGFrame, matrix, aPoint); + return clipPathFrame->ClipHitTest(aFrame, matrix, aPoint); } nsIFrame * @@ -1327,6 +1372,58 @@ nsSVGUtils::GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut) ? NS_OK : NS_ERROR_FAILURE; } +already_AddRefed +nsSVGUtils::GetBBox(nsIFrame *aFrame) +{ + nsISVGChildFrame *svg; + CallQueryInterface(aFrame, &svg); + if (!svg) { + nsIDOMSVGRect *rect = nsnull; + gfxRect r = nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame); + NS_NewSVGRect(&rect, r); + return rect; + } + + PRBool needToDisablePropagation = svg->GetMatrixPropagation(); + if (needToDisablePropagation) { + svg->SetMatrixPropagation(PR_FALSE); + svg->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION | + nsISVGChildFrame::TRANSFORM_CHANGED); + } + + nsCOMPtr bbox; + svg->GetBBox(getter_AddRefs(bbox)); + + if (needToDisablePropagation) { + svg->SetMatrixPropagation(PR_TRUE); + svg->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION | + nsISVGChildFrame::TRANSFORM_CHANGED); + } + + return bbox.forget(); +} + +gfxRect +nsSVGUtils::GetRelativeRect(PRUint16 aUnits, const nsSVGLength2 *aXYWH, + nsIDOMSVGRect *aBBox, nsIFrame *aFrame) +{ + float x, y, width, height; + if (aUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { + aBBox->GetX(&x); + x += ObjectSpace(aBBox, &aXYWH[0]); + aBBox->GetY(&y); + y += ObjectSpace(aBBox, &aXYWH[1]); + width = ObjectSpace(aBBox, &aXYWH[2]); + height = ObjectSpace(aBBox, &aXYWH[3]); + } else { + x = nsSVGUtils::UserSpace(aFrame, &aXYWH[0]); + y = nsSVGUtils::UserSpace(aFrame, &aXYWH[1]); + width = nsSVGUtils::UserSpace(aFrame, &aXYWH[2]); + height = nsSVGUtils::UserSpace(aFrame, &aXYWH[3]); + } + return gfxRect(x, y, width, height); +} + PRBool nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame) { @@ -1363,33 +1460,45 @@ nsSVGUtils::MaxExpansion(nsIDOMSVGMatrix *aMatrix) already_AddRefed nsSVGUtils::AdjustMatrixForUnits(nsIDOMSVGMatrix *aMatrix, nsSVGEnum *aUnits, - nsISVGChildFrame *aFrame) + nsIFrame *aFrame) { nsCOMPtr fini = aMatrix; if (aFrame && aUnits->GetAnimValue() == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { - nsCOMPtr rect; - nsresult rv = aFrame->GetBBox(getter_AddRefs(rect)); + float minx, miny, width, height; - if (NS_SUCCEEDED(rv)) { - float minx, miny, width, height; - rect->GetX(&minx); - rect->GetY(&miny); - rect->GetWidth(&width); - rect->GetHeight(&height); - - // Correct for scaling in outersvg CTM - nsIFrame *frame; - CallQueryInterface(aFrame, &frame); - nsPresContext *presCtx = frame->PresContext(); - float cssPxPerDevPx = - presCtx->AppUnitsToFloatCSSPixels(presCtx->AppUnitsPerDevPixel()); - minx *= cssPxPerDevPx; - miny *= cssPxPerDevPx; - width *= cssPxPerDevPx; - height *= cssPxPerDevPx; + PRBool gotRect = PR_FALSE; + if (aFrame->IsFrameOfType(nsIFrame::eSVG)) { + nsISVGChildFrame *svgFrame; + CallQueryInterface(aFrame, &svgFrame); + nsCOMPtr rect; + svgFrame->GetBBox(getter_AddRefs(rect)); + if (rect) { + gotRect = PR_TRUE; + rect->GetX(&minx); + rect->GetY(&miny); + rect->GetWidth(&width); + rect->GetHeight(&height); + // Correct for scaling in outersvg CTM + nsPresContext *presCtx = aFrame->PresContext(); + float scaleInv = + presCtx->AppUnitsToGfxUnits(presCtx->AppUnitsPerCSSPixel()); + minx /= scaleInv; + miny /= scaleInv; + width /= scaleInv; + height /= scaleInv; + } + } else { + gotRect = PR_TRUE; + gfxRect r = nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame); + minx = r.X(); + miny = r.Y(); + width = r.Width(); + height = r.Height(); + } + if (gotRect) { nsCOMPtr tmp; aMatrix->Translate(minx, miny, getter_AddRefs(tmp)); tmp->ScaleNonUniform(width, height, getter_AddRefs(fini)); @@ -1444,6 +1553,8 @@ nsSVGRenderState::GetRenderingContext(nsIFrame *aFrame) if (!mRenderingContext) { nsIDeviceContext* devCtx = aFrame->PresContext()->DeviceContext(); devCtx->CreateRenderingContextInstance(*getter_AddRefs(mRenderingContext)); + if (!mRenderingContext) + return nsnull; mRenderingContext->Init(devCtx, mGfxContext); } return mRenderingContext; diff --git a/layout/svg/base/src/nsSVGUtils.h b/layout/svg/base/src/nsSVGUtils.h index 561f42b438d9..5453f6e14355 100644 --- a/layout/svg/base/src/nsSVGUtils.h +++ b/layout/svg/base/src/nsSVGUtils.h @@ -100,6 +100,8 @@ class nsISVGChildFrame; /* are we the child of a non-display container? */ #define NS_STATE_SVG_NONDISPLAY_CHILD 0x02000000 +#define NS_STATE_SVG_PROPAGATE_TRANSFORM 0x04000000 + /** * Byte offsets of channels in a native packed gfxColor or cairo image surface. */ @@ -192,11 +194,12 @@ public: * Get a font-size (em) of an nsIContent */ static float GetFontSize(nsIContent *aContent); - + static float GetFontSize(nsIFrame *aFrame); /* * Get an x-height of of an nsIContent */ static float GetFontXHeight(nsIContent *aContent); + static float GetFontXHeight(nsIFrame *aFrame); /* * Converts image data from premultipled to unpremultiplied alpha @@ -299,13 +302,19 @@ public: Input: rect - bounding box length - length to be converted */ - static float ObjectSpace(nsIDOMSVGRect *aRect, nsSVGLength2 *aLength); + static float ObjectSpace(nsIDOMSVGRect *aRect, const nsSVGLength2 *aLength); /* Computes the input length in terms of user space coordinates. Input: content - object to be used for determining user space + Input: length - length to be converted + */ + static float UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength); + + /* Computes the input length in terms of user space coordinates. + Input: aFrame - object to be used for determining user space length - length to be converted */ - static float UserSpace(nsSVGElement *aSVGElement, nsSVGLength2 *aLength); + static float UserSpace(nsIFrame *aFrame, const nsSVGLength2 *aLength); /* Tranforms point by the matrix. In/out: x,y */ static void @@ -337,8 +346,8 @@ public: nsIDOMSVGAnimatedPreserveAspectRatio *aPreserveAspectRatio, PRBool aIgnoreAlign = PR_FALSE); - /* Paint frame with SVG effects - aDirtyRect is the area being - * redrawn, in frame offset pixel coordinates */ + /* Paint SVG frame with SVG effects - aDirtyRect is the area being + * redrawn, in device pixel coordinates relative to the outer svg */ static void PaintChildWithEffects(nsSVGRenderState *aContext, nsIntRect *aDirtyRect, @@ -371,7 +380,8 @@ public: /* * Returns the CanvasTM of the indicated frame, whether it's a - * child or container SVG frame. + * child SVG frame, container SVG frame, or a regular frame. + * For regular frames, we just return an identity matrix. */ static already_AddRefed GetCanvasTM(nsIFrame *aFrame); @@ -469,7 +479,26 @@ public: static already_AddRefed AdjustMatrixForUnits(nsIDOMSVGMatrix *aMatrix, nsSVGEnum *aUnits, - nsISVGChildFrame *aFrame); + nsIFrame *aFrame); + + /** + * Get bounding-box for aFrame. Matrix propagation is disabled so the + * bounding box is computed in terms of aFrame's own user space. + */ + static already_AddRefed + GetBBox(nsIFrame *aFrame); + /** + * Compute a rectangle in userSpaceOnUse or objectBoundingBoxUnits. + * @param aXYWH pointer to 4 consecutive nsSVGLength2 objects containing + * the x, y, width and height values in that order + * @param aBBox the bounding box of the object the rect is relative to; + * may be null if aUnits is not SVG_UNIT_TYPE_OBJECTBOUNDINGBOX + * @param aFrame the object in which to interpret user-space units; + * may be null if aUnits is SVG_UNIT_TYPE_OBJECTBOUNDINGBOX + */ + static gfxRect + GetRelativeRect(PRUint16 aUnits, const nsSVGLength2 *aXYWH, nsIDOMSVGRect *aBBox, + nsIFrame *aFrame); #ifdef DEBUG static void