mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-02 07:05:24 +00:00
Bug 738192 - Get rid of the old horrendous "invalidate everything" hack in nsSVGUtils::FindFilterInvalidation(). r=longsonr.
--HG-- extra : rebase_source : 5c26b1320e1a0613c1b71b7c12c86db83316dcdd
This commit is contained in:
parent
54f5a211be
commit
25d4e26503
@ -15,6 +15,7 @@
|
||||
#include "nsSVGFilterElement.h"
|
||||
#include "nsSVGFilterInstance.h"
|
||||
#include "nsSVGFilterPaintCallback.h"
|
||||
#include "nsSVGIntegrationUtils.h"
|
||||
#include "nsSVGUtils.h"
|
||||
|
||||
nsIFrame*
|
||||
@ -25,6 +26,10 @@ NS_NewSVGFilterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
|
||||
|
||||
NS_IMPL_FRAMEARENA_HELPERS(nsSVGFilterFrame)
|
||||
|
||||
/**
|
||||
* Returns the entire filter size if aDeviceRect is null, or if
|
||||
* the result is too large to be stored in an nsIntRect.
|
||||
*/
|
||||
static nsIntRect
|
||||
MapDeviceRectToFilterSpace(const gfxMatrix& aMatrix,
|
||||
const gfxIntSize& aFilterSize,
|
||||
@ -404,6 +409,10 @@ nsSVGFilterFrame::PaintFilteredFrame(nsRenderingContext *aContext,
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns NS_ERROR_FAILURE if the result is too large to be stored
|
||||
* in an nsIntRect.
|
||||
*/
|
||||
static nsresult
|
||||
TransformFilterSpaceToDeviceSpace(nsSVGFilterInstance *aInstance,
|
||||
nsIntRect *aRect)
|
||||
@ -423,8 +432,21 @@ nsIntRect
|
||||
nsSVGFilterFrame::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
|
||||
const nsIntRect& aPreFilterDirtyRect)
|
||||
{
|
||||
bool overrideCTM = false;
|
||||
gfxMatrix ctm;
|
||||
|
||||
if (aFilteredFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
|
||||
// In the case of this method we want to input a rect that is relative to
|
||||
// aFilteredFrame and get back a rect that is relative to aFilteredFrame.
|
||||
// To do that we need to provide an override canvanTM to prevent the filter
|
||||
// code from calling GetCanvasTM and using that TM as normal.
|
||||
overrideCTM = true;
|
||||
ctm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFilteredFrame);
|
||||
}
|
||||
|
||||
nsAutoFilterInstance instance(aFilteredFrame, this, nsnull, nsnull,
|
||||
&aPreFilterDirtyRect, nsnull);
|
||||
&aPreFilterDirtyRect, nsnull,
|
||||
overrideCTM ? &ctm : nsnull);
|
||||
if (!instance.get())
|
||||
return nsIntRect();
|
||||
|
||||
@ -478,11 +500,7 @@ nsSVGFilterFrame::GetPostFilterBounds(nsIFrame *aFilteredFrame,
|
||||
// rect relative to aTarget itself. For that we need to prevent the filter
|
||||
// code using GetCanvasTM().
|
||||
overrideCTM = true;
|
||||
PRInt32 appUnitsPerDevPixel =
|
||||
aFilteredFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
float devPxPerCSSPx =
|
||||
1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
|
||||
ctm.Scale(devPxPerCSSPx, devPxPerCSSPx);
|
||||
ctm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFilteredFrame);
|
||||
}
|
||||
|
||||
nsAutoFilterInstance instance(aFilteredFrame, this, nsnull, nsnull,
|
||||
|
@ -175,7 +175,7 @@ nsSVGForeignObjectFrame::InvalidateInternal(const nsRect& aDamageRect,
|
||||
if (!mInReflow) {
|
||||
// We can't collect dirty areas, since we don't have a place to reliably
|
||||
// call FlushDirtyRegion before we paint, so we have to invalidate now.
|
||||
InvalidateDirtyRect(nsSVGUtils::GetOuterSVGFrame(this), aDamageRect + nsPoint(aX, aY), aFlags);
|
||||
InvalidateDirtyRect(aDamageRect + nsPoint(aX, aY), aFlags, false);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -578,13 +578,16 @@ nsSVGForeignObjectFrame::DoReflow()
|
||||
mInReflow = false;
|
||||
|
||||
if (!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
|
||||
FlushDirtyRegion(0);
|
||||
// Since we're a reflow root and can be reflowed independently of our
|
||||
// outer-<svg>, we can't just blindly pass 'true' here.
|
||||
FlushDirtyRegion(0, nsSVGUtils::OuterSVGIsCallingUpdateBounds(this));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGForeignObjectFrame::InvalidateDirtyRect(nsSVGOuterSVGFrame* aOuter,
|
||||
const nsRect& aRect, PRUint32 aFlags)
|
||||
nsSVGForeignObjectFrame::InvalidateDirtyRect(const nsRect& aRect,
|
||||
PRUint32 aFlags,
|
||||
bool aDuringReflowSVG)
|
||||
{
|
||||
if (aRect.IsEmpty())
|
||||
return;
|
||||
@ -594,19 +597,12 @@ nsSVGForeignObjectFrame::InvalidateDirtyRect(nsSVGOuterSVGFrame* aOuter,
|
||||
if (rect.IsEmpty())
|
||||
return;
|
||||
|
||||
// The areas dirtied by children are in app units, relative to this frame.
|
||||
// We need to convert the rect from app units in our userspace to app units
|
||||
// relative to our nsSVGOuterSVGFrame's content rect.
|
||||
|
||||
gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
|
||||
r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
|
||||
rect = ToCanvasBounds(r, GetCanvasTM(), PresContext());
|
||||
rect = nsSVGUtils::FindFilterInvalidation(this, rect);
|
||||
aOuter->InvalidateWithFlags(rect, aFlags);
|
||||
nsSVGUtils::InvalidateBounds(this, aDuringReflowSVG, &rect, aFlags);
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGForeignObjectFrame::FlushDirtyRegion(PRUint32 aFlags)
|
||||
nsSVGForeignObjectFrame::FlushDirtyRegion(PRUint32 aFlags,
|
||||
bool aDuringReflowSVG)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
|
||||
"Should not have been called");
|
||||
@ -618,15 +614,12 @@ nsSVGForeignObjectFrame::FlushDirtyRegion(PRUint32 aFlags)
|
||||
return;
|
||||
}
|
||||
|
||||
nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(this);
|
||||
if (!outerSVGFrame) {
|
||||
NS_ERROR("null outerSVGFrame");
|
||||
return;
|
||||
}
|
||||
|
||||
InvalidateDirtyRect(outerSVGFrame, mSameDocDirtyRegion.GetBounds(), aFlags);
|
||||
InvalidateDirtyRect(outerSVGFrame, mSubDocDirtyRegion.GetBounds(),
|
||||
aFlags | INVALIDATE_CROSS_DOC);
|
||||
InvalidateDirtyRect(mSameDocDirtyRegion.GetBounds(),
|
||||
aFlags,
|
||||
aDuringReflowSVG);
|
||||
InvalidateDirtyRect(mSubDocDirtyRegion.GetBounds(),
|
||||
aFlags | INVALIDATE_CROSS_DOC,
|
||||
aDuringReflowSVG);
|
||||
|
||||
mSameDocDirtyRegion.SetEmpty();
|
||||
mSubDocDirtyRegion.SetEmpty();
|
||||
|
@ -93,9 +93,9 @@ protected:
|
||||
// Returns GetCanvasTM followed by a scale from CSS px to Dev px. Used for
|
||||
// painting, because children expect to paint to device space, not userspace.
|
||||
gfxMatrix GetCanvasTMForChildren();
|
||||
void InvalidateDirtyRect(nsSVGOuterSVGFrame* aOuter,
|
||||
const nsRect& aRect, PRUint32 aFlags);
|
||||
void FlushDirtyRegion(PRUint32 aFlags);
|
||||
void InvalidateDirtyRect(const nsRect& aRect, PRUint32 aFlags,
|
||||
bool aDuringReflowSVG);
|
||||
void FlushDirtyRegion(PRUint32 aFlags, bool aDuringReflowSVG);
|
||||
|
||||
// If width or height is less than or equal to zero we must disable rendering
|
||||
bool IsDisabled() const { return mRect.width <= 0 || mRect.height <= 0; }
|
||||
|
@ -494,8 +494,6 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx,
|
||||
gfxMatrix
|
||||
nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame)
|
||||
{
|
||||
NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
|
||||
"SVG frames should not get here");
|
||||
PRInt32 appUnitsPerDevPixel = aNonSVGFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
float devPxPerCSSPx =
|
||||
1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
|
||||
|
@ -396,9 +396,7 @@ nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext,
|
||||
// Now that we've marked the necessary children as dirty, call
|
||||
// UpdateBounds() on them:
|
||||
|
||||
#ifdef DEBUG
|
||||
mCallingUpdateBounds = true;
|
||||
#endif
|
||||
|
||||
if (!(mState & NS_STATE_SVG_NONDISPLAY_CHILD)) {
|
||||
nsIFrame* kid = mFrames.FirstChild();
|
||||
@ -411,9 +409,7 @@ nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext,
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
mCallingUpdateBounds = false;
|
||||
#endif
|
||||
|
||||
// Make sure we scroll if we're too big:
|
||||
// XXX Use the bounding box of our descendants? (See bug 353460 comment 14.)
|
||||
|
@ -111,17 +111,13 @@ public:
|
||||
*/
|
||||
bool VerticalScrollbarNotNeeded() const;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool IsCallingUpdateBounds() const {
|
||||
return mCallingUpdateBounds;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mCallingUpdateBounds;
|
||||
#endif
|
||||
|
||||
/* Returns true if our content is the document element and our document is
|
||||
* embedded in an HTML 'object', 'embed' or 'applet' element. Set
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "nsCSSFrameConstructor.h"
|
||||
#include "nsComputedDOMStyle.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDisplayList.h"
|
||||
#include "nsFrameList.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsIContent.h"
|
||||
@ -598,80 +599,15 @@ nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
|
||||
return r;
|
||||
}
|
||||
|
||||
nsRect
|
||||
nsSVGUtils::FindFilterInvalidation(nsIFrame *aFrame, const nsRect& aRect)
|
||||
{
|
||||
PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
nsIntRect rect = aRect.ToOutsidePixels(appUnitsPerDevPixel);
|
||||
|
||||
while (aFrame) {
|
||||
if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
|
||||
break;
|
||||
|
||||
nsSVGFilterFrame *filter = nsSVGEffects::GetFilterFrame(aFrame);
|
||||
if (filter) {
|
||||
// When we are under AttributeChanged, we can no longer get the old bbox
|
||||
// by calling GetBBox(), and we need that to set up the filter region
|
||||
// with the correct position. :-(
|
||||
//rect = filter->GetInvalidationBBox(aFrame, rect);
|
||||
|
||||
// XXX [perf] As a horrible workaround, for now we just invalidate the
|
||||
// entire area of the nearest viewport establishing frame that doesnt
|
||||
// have overflow:visible. See bug 463939.
|
||||
nsSVGDisplayContainerFrame* viewportFrame = GetNearestSVGViewport(aFrame);
|
||||
while (viewportFrame && !viewportFrame->GetStyleDisplay()->IsScrollableOverflow()) {
|
||||
viewportFrame = GetNearestSVGViewport(viewportFrame);
|
||||
}
|
||||
if (!viewportFrame) {
|
||||
viewportFrame = GetOuterSVGFrame(aFrame);
|
||||
}
|
||||
if (viewportFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
|
||||
nsRect r = viewportFrame->GetVisualOverflowRect();
|
||||
// GetOverflowRect is relative to our border box, but we need it
|
||||
// relative to our content box.
|
||||
r.MoveBy(viewportFrame->GetPosition() - viewportFrame->GetContentRect().TopLeft());
|
||||
return r;
|
||||
}
|
||||
NS_ASSERTION(viewportFrame->GetType() == nsGkAtoms::svgInnerSVGFrame,
|
||||
"Wrong frame type");
|
||||
nsSVGInnerSVGFrame* innerSvg = do_QueryFrame(static_cast<nsIFrame*>(viewportFrame));
|
||||
nsSVGDisplayContainerFrame* innerSvgParent = do_QueryFrame(viewportFrame->GetParent());
|
||||
float x, y, width, height;
|
||||
static_cast<nsSVGSVGElement*>(innerSvg->GetContent())->
|
||||
GetAnimatedLengthValues(&x, &y, &width, &height, nsnull);
|
||||
gfxRect bounds = GetCanvasTM(innerSvgParent).
|
||||
TransformBounds(gfxRect(x, y, width, height));
|
||||
bounds.RoundOut();
|
||||
nsIntRect r;
|
||||
if (gfxUtils::GfxRectToIntRect(bounds, &r)) {
|
||||
rect = r;
|
||||
} else {
|
||||
NS_NOTREACHED("Not going to invalidate the correct area");
|
||||
}
|
||||
aFrame = viewportFrame;
|
||||
}
|
||||
aFrame = aFrame->GetParent();
|
||||
}
|
||||
|
||||
nsRect r = rect.ToAppUnits(appUnitsPerDevPixel);
|
||||
if (aFrame) {
|
||||
NS_ASSERTION(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG,
|
||||
"outer SVG frame expected");
|
||||
r.MoveBy(aFrame->GetContentRect().TopLeft() - aFrame->GetPosition());
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
nsSVGUtils::OuterSVGIsCallingUpdateBounds(nsIFrame *aFrame)
|
||||
{
|
||||
return nsSVGUtils::GetOuterSVGFrame(aFrame)->IsCallingUpdateBounds();
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
nsSVGUtils::InvalidateBounds(nsIFrame *aFrame, bool aDuringUpdate)
|
||||
nsSVGUtils::InvalidateBounds(nsIFrame *aFrame, bool aDuringUpdate,
|
||||
const nsRect *aBoundsSubArea, PRUint32 aFlags)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aFrame->IsFrameOfType(nsIFrame::eSVG),
|
||||
"Passed bad frame!");
|
||||
@ -707,23 +643,70 @@ nsSVGUtils::InvalidateBounds(nsIFrame *aFrame, bool aDuringUpdate)
|
||||
return;
|
||||
}
|
||||
|
||||
// XXXSDL we want to reduce the bounds when passing through inner-<svg>
|
||||
// and <use>, etc.
|
||||
// Okay, so now we pass the area that needs to be invalidated up our parent
|
||||
// chain, accounting for filter effects and transforms as we go, until we
|
||||
// reach our nsSVGOuterSVGFrame where we can invalidate:
|
||||
|
||||
nsSVGOuterSVGFrame* outerSVGFrame = GetOuterSVGFrame(aFrame);
|
||||
NS_ASSERTION(outerSVGFrame, "no outer svg frame");
|
||||
if (outerSVGFrame) {
|
||||
nsISVGChildFrame *svgFrame = do_QueryFrame(aFrame);
|
||||
if (!svgFrame)
|
||||
return;
|
||||
|
||||
// Note that filters can paint even if the element being filtered has empty
|
||||
// bounds, so we don't return early for that. Specifically, filters can be
|
||||
// given an explicit size that doesn't depend on the bbox of the element
|
||||
// being filtered, and then feFlood can be used to fill that area with paint.
|
||||
nsRect rect = FindFilterInvalidation(aFrame, svgFrame->GetCoveredRegion());
|
||||
outerSVGFrame->Invalidate(rect);
|
||||
nsRect invalidArea;
|
||||
if (aBoundsSubArea) {
|
||||
invalidArea = *aBoundsSubArea;
|
||||
} else {
|
||||
invalidArea = aFrame->GetVisualOverflowRect();
|
||||
// GetVisualOverflowRect() already includes filter effects and transforms,
|
||||
// so advance to our parent before the loop below:
|
||||
invalidArea += aFrame->GetPosition();
|
||||
aFrame = aFrame->GetParent();
|
||||
}
|
||||
|
||||
PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
|
||||
PRInt32 appUnitsPerCSSPx = aFrame->PresContext()->AppUnitsPerCSSPixel();
|
||||
|
||||
while (aFrame) {
|
||||
if ((aFrame->GetStateBits() & NS_FRAME_IS_DIRTY)) {
|
||||
// This ancestor frame has already been invalidated, so nothing to do.
|
||||
return;
|
||||
}
|
||||
if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
|
||||
break;
|
||||
}
|
||||
if (aFrame->GetType() == nsGkAtoms::svgInnerSVGFrame &&
|
||||
aFrame->GetStyleDisplay()->IsScrollableOverflow()) {
|
||||
// Clip rect to the viewport established by this inner-<svg>:
|
||||
float x, y, width, height;
|
||||
static_cast<nsSVGSVGElement*>(aFrame->GetContent())->
|
||||
GetAnimatedLengthValues(&x, &y, &width, &height, nsnull);
|
||||
if (width <= 0.0f || height <= 0.0f) {
|
||||
return; // Nothing to invalidate
|
||||
}
|
||||
nsRect viewportRect =
|
||||
nsLayoutUtils::RoundGfxRectToAppRect(gfxRect(0.0, 0.0, width, height),
|
||||
appUnitsPerCSSPx);
|
||||
invalidArea = invalidArea.Intersect(viewportRect);
|
||||
if (invalidArea.IsEmpty()) {
|
||||
return; // Nothing to invalidate
|
||||
}
|
||||
}
|
||||
nsSVGFilterFrame *filterFrame = nsSVGEffects::GetFilterFrame(aFrame);
|
||||
if (filterFrame) {
|
||||
invalidArea =
|
||||
filterFrame->GetPostFilterDirtyArea(aFrame,
|
||||
invalidArea.ToOutsidePixels(appUnitsPerDevPixel)).
|
||||
ToAppUnits(appUnitsPerDevPixel);
|
||||
}
|
||||
if (aFrame->IsTransformed()) {
|
||||
invalidArea =
|
||||
nsDisplayTransform::TransformRect(invalidArea, aFrame, nsPoint(0, 0));
|
||||
}
|
||||
invalidArea += aFrame->GetPosition();
|
||||
aFrame = aFrame->GetParent();
|
||||
}
|
||||
|
||||
NS_ASSERTION(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG,
|
||||
"SVG frames must always have an nsSVGOuterSVGFrame ancestor!");
|
||||
invalidArea.MoveBy(aFrame->GetContentRect().TopLeft() - aFrame->GetPosition());
|
||||
|
||||
static_cast<nsSVGOuterSVGFrame*>(aFrame)->InvalidateWithFlags(invalidArea,
|
||||
aFlags);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -327,25 +327,20 @@ public:
|
||||
static nsRect GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
|
||||
const nsRect &aUnfilteredRect);
|
||||
|
||||
/**
|
||||
* Figures out the worst case invalidation area for a frame, taking
|
||||
* filters into account.
|
||||
* Note that the caller is responsible for making sure that any cached
|
||||
* covered regions in the frame tree rooted at aFrame are up to date.
|
||||
* @param aRect the area in app units that needs to be invalidated in aFrame
|
||||
* @return the rect in app units that should be invalidated, taking
|
||||
* filters into account. Will return aRect when no filters are present.
|
||||
*/
|
||||
static nsRect FindFilterInvalidation(nsIFrame *aFrame, const nsRect& aRect);
|
||||
|
||||
/**
|
||||
* Invalidates the area that is painted by the frame without updating its
|
||||
* bounds.
|
||||
*
|
||||
* This is similar to InvalidateOverflowRect(). It will go away when we
|
||||
* support display list based invalidation of SVG.
|
||||
*
|
||||
* @param aBoundsSubArea If non-null, a sub-area of aFrame's pre-filter
|
||||
* visual overflow rect that should be invalidated instead of aFrame's
|
||||
* entire visual overflow rect.
|
||||
*/
|
||||
static void InvalidateBounds(nsIFrame *aFrame, bool aDuringUpdate = false);
|
||||
static void InvalidateBounds(nsIFrame *aFrame, bool aDuringUpdate = false,
|
||||
const nsRect *aBoundsSubArea = nsnull,
|
||||
PRUint32 aFlags = 0);
|
||||
|
||||
/**
|
||||
* Schedules an update of the frame's bounds (which will in turn invalidate
|
||||
@ -621,9 +616,9 @@ public:
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
WritePPM(const char *fname, gfxImageSurface *aSurface);
|
||||
#endif
|
||||
|
||||
static bool OuterSVGIsCallingUpdateBounds(nsIFrame *aFrame);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Get any additional transforms that apply only to stroking
|
||||
|
Loading…
Reference in New Issue
Block a user