Bug 767823 - Make nsAutoFilterInstance take frame rects and stop using GetCoveredRegion. r=longsonr.

This commit is contained in:
Jonathan Watt 2012-06-26 11:49:23 +01:00
parent ddf6acd6f8
commit 678c10801d
8 changed files with 259 additions and 192 deletions

View File

@ -5001,6 +5001,14 @@ nsIFrame::GetVisualOverflowRectRelativeToSelf() const
return GetVisualOverflowRect();
}
nsRect
nsIFrame::GetPreEffectsVisualOverflowRect() const
{
nsRect* r = static_cast<nsRect*>
(Properties().Get(nsIFrame::PreEffectsBBoxProperty()));
return r ? *r : GetVisualOverflowRectRelativeToSelf();
}
/* virtual */ bool
nsFrame::UpdateOverflow()
{

View File

@ -2298,6 +2298,13 @@ public:
*/
nsRect GetVisualOverflowRectRelativeToSelf() const;
/**
* Returns this frame's visual overflow rect as it would be before taking
* account of SVG effects or transforms. The rect returned is relative to
* this frame.
*/
nsRect GetPreEffectsVisualOverflowRect() const;
/**
* Store the overflow area in the frame's mOverflow.mVisualDeltas
* fields or as a frame property in the frame manager so that it can

View File

@ -12,6 +12,7 @@
#include "nsGkAtoms.h"
#include "nsRenderingContext.h"
#include "nsSVGEffects.h"
#include "nsSVGElement.h"
#include "nsSVGFilterElement.h"
#include "nsSVGFilterInstance.h"
#include "nsSVGFilterPaintCallback.h"
@ -27,27 +28,70 @@ 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.
* Converts an nsRect that is relative to a filtered frame's origin (i.e. the
* top-left corner of its border box) into filter space.
* Returns the entire filter region (a rect the width/height of aFilterRes) if
* aFrameRect is null, or if the result is too large to be stored in an
* nsIntRect.
*/
static nsIntRect
MapDeviceRectToFilterSpace(const gfxMatrix& aMatrix,
const gfxIntSize& aFilterSize,
const nsIntRect* aDeviceRect)
MapFrameRectToFilterSpace(const nsRect* aRect,
PRInt32 aAppUnitsPerCSSPx,
const gfxMatrix& aFrameSpaceInCSSPxToFilterSpace,
const gfxIntSize& aFilterRes)
{
nsIntRect rect(0, 0, aFilterSize.width, aFilterSize.height);
if (aDeviceRect) {
gfxRect r = aMatrix.TransformBounds(gfxRect(aDeviceRect->x, aDeviceRect->y,
aDeviceRect->width, aDeviceRect->height));
r.RoundOut();
nsIntRect rect(0, 0, aFilterRes.width, aFilterRes.height);
if (aRect) {
gfxRect rectInCSSPx =
nsLayoutUtils::RectToGfxRect(*aRect, aAppUnitsPerCSSPx);
gfxRect rectInFilterSpace =
aFrameSpaceInCSSPxToFilterSpace.TransformBounds(rectInCSSPx);
rectInFilterSpace.RoundOut();
nsIntRect intRect;
if (gfxUtils::GfxRectToIntRect(r, &intRect)) {
if (gfxUtils::GfxRectToIntRect(rectInFilterSpace, &intRect)) {
rect = intRect;
}
}
return rect;
}
/**
* Returns the transform from frame space to the coordinate space that
* GetCanvasTM transforms to. "Frame space" is the origin of a frame, aka the
* top-left corner of its border box, aka the top left corner of its mRect.
*/
static gfxMatrix
GetUserToFrameSpaceInCSSPxTransform(nsIFrame *aFrame)
{
gfxMatrix userToFrameSpaceInCSSPx;
if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
PRInt32 appUnitsPerCSSPx = aFrame->PresContext()->AppUnitsPerCSSPixel();
// As currently implemented by Mozilla for the purposes of filters, user
// space is the coordinate system established by GetCanvasTM(), since
// that's what we use to set filterToDeviceSpace above. In other words,
// for SVG, user space is actually the coordinate system aTarget
// establishes for _its_ children (i.e. after taking account of any x/y
// and viewBox attributes), not the coordinate system that is established
// for it by its 'transform' attribute (or by its _parent_) as it's
// normally defined. (XXX We should think about fixing this.) The only
// frame type for which these extra transforms are not simply an x/y
// translation is nsSVGInnerSVGFrame, hence we treat it specially here.
if (aFrame->GetType() == nsGkAtoms::svgInnerSVGFrame) {
userToFrameSpaceInCSSPx =
static_cast<nsSVGElement*>(aFrame->GetContent())->
PrependLocalTransformsTo(gfxMatrix());
} else {
gfxPoint targetsUserSpaceOffset =
nsLayoutUtils::RectToGfxRect(aFrame->GetRect(), appUnitsPerCSSPx).
TopLeft();
userToFrameSpaceInCSSPx.Translate(-targetsUserSpaceOffset);
}
}
// else, for all other frames, leave as the identity matrix
return userToFrameSpaceInCSSPx;
}
class nsSVGFilterFrame::AutoFilterReferencer
{
public:
@ -71,10 +115,10 @@ public:
nsAutoFilterInstance(nsIFrame *aTarget,
nsSVGFilterFrame *aFilterFrame,
nsSVGFilterPaintCallback *aPaint,
const nsIntRect *aPostFilterDirtyRect,
const nsIntRect *aPreFilterDirtyRect,
const gfxRect *aOverrideBBox,
const gfxMatrix *aOverrideUserToDeviceSpace = nsnull);
const nsRect *aPostFilterDirtyRect,
const nsRect *aPreFilterDirtyRect,
const nsRect *aOverridePreFilterVisualOverflowRect,
const gfxRect *aOverrideBBox = nsnull);
~nsAutoFilterInstance() {}
// If this returns null, then draw nothing. Either the filter draws
@ -88,10 +132,10 @@ private:
nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
nsSVGFilterFrame *aFilterFrame,
nsSVGFilterPaintCallback *aPaint,
const nsIntRect *aPostFilterDirtyRect,
const nsIntRect *aPreFilterDirtyRect,
const gfxRect *aOverrideBBox,
const gfxMatrix *aOverrideUserToDeviceSpace)
const nsRect *aPostFilterDirtyRect,
const nsRect *aPreFilterDirtyRect,
const nsRect *aPreFilterVisualOverflowRectOverride,
const gfxRect *aOverrideBBox)
{
const nsSVGFilterElement *filter = aFilterFrame->GetFilterContent();
@ -122,6 +166,7 @@ nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
XYWH[1] = *aFilterFrame->GetLengthValue(nsSVGFilterElement::Y);
XYWH[2] = *aFilterFrame->GetLengthValue(nsSVGFilterElement::WIDTH);
XYWH[3] = *aFilterFrame->GetLengthValue(nsSVGFilterElement::HEIGHT);
// The filter region in user space, in user units:
gfxRect filterRegion = nsSVGUtils::GetRelativeRect(filterUnits,
XYWH, bbox, aTarget);
@ -131,15 +176,11 @@ nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
return;
}
gfxMatrix userToDeviceSpace;
if (aOverrideUserToDeviceSpace) {
userToDeviceSpace = *aOverrideUserToDeviceSpace;
} else {
userToDeviceSpace = nsSVGUtils::GetCanvasTM(aTarget);
}
// Calculate filterRes (the width and height of the pixel buffer of the
// temporary offscreen surface that we'll paint into):
// temporary offscreen surface that we would/will create to paint into when
// painting the entire filtered element) and, if necessary, adjust
// filterRegion out slightly so that it aligns with pixel boundaries of this
// buffer:
gfxIntSize filterRes;
const nsSVGIntegerPair* filterResAttrs =
@ -183,52 +224,55 @@ nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
filterRegion.Scale(1.0 / scale);
}
// XXX we haven't taken account of the fact that filterRegion may be
// partially or entirely outside the current clip region. :-/
// Convert the dirty rects to filter space, and create our nsSVGFilterInstance:
// Get various transforms:
gfxMatrix filterToUserSpace(filterRegion.Width() / filterRes.width, 0.0f,
0.0f, filterRegion.Height() / filterRes.height,
filterRegion.X(), filterRegion.Y());
gfxMatrix filterToDeviceSpace = filterToUserSpace * userToDeviceSpace;
// filterToDeviceSpace is always invertible
gfxMatrix deviceToFilterSpace = filterToDeviceSpace;
deviceToFilterSpace.Invert();
// Only used (so only set) when we paint:
gfxMatrix filterToDeviceSpace;
if (aPaint) {
filterToDeviceSpace =
filterToUserSpace * nsSVGUtils::GetCanvasTM(aTarget);
}
// Convert the passed in rects from frame to filter space:
PRInt32 appUnitsPerCSSPx = aTarget->PresContext()->AppUnitsPerCSSPixel();
gfxMatrix filterToFrameSpaceInCSSPx =
filterToUserSpace * GetUserToFrameSpaceInCSSPxTransform(aTarget);
// filterToFrameSpaceInCSSPx is always invertible
gfxMatrix frameSpaceInCSSPxTofilterSpace = filterToFrameSpaceInCSSPx;
frameSpaceInCSSPxTofilterSpace.Invert();
nsIntRect postFilterDirtyRect =
MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, aPostFilterDirtyRect);
MapFrameRectToFilterSpace(aPostFilterDirtyRect, appUnitsPerCSSPx,
frameSpaceInCSSPxTofilterSpace, filterRes);
nsIntRect preFilterDirtyRect =
MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, aPreFilterDirtyRect);
nsIntRect targetBoundsDeviceSpace;
nsISVGChildFrame* svgTarget = do_QueryFrame(aTarget);
if (svgTarget) {
if (aOverrideUserToDeviceSpace) {
// If aOverrideUserToDeviceSpace is specified, it is a simple
// CSS-px-to-dev-px transform passed by nsSVGFilterFrame::
// GetPostFilterBounds() when requesting the filter expansion of the
// overflow rects in frame space. In this case GetCoveredRegion() is not
// what we want since it is in outer-<svg> space, so GetPostFilterBounds
// passes in the pre-filter bounds of the frame in frame space for us to
// use instead.
NS_ASSERTION(aPreFilterDirtyRect, "Who passed aOverrideUserToDeviceSpace?");
targetBoundsDeviceSpace = *aPreFilterDirtyRect;
} else {
targetBoundsDeviceSpace =
svgTarget->GetCoveredRegion().ToOutsidePixels(aTarget->
PresContext()->AppUnitsPerDevPixel());
}
MapFrameRectToFilterSpace(aPreFilterDirtyRect, appUnitsPerCSSPx,
frameSpaceInCSSPxTofilterSpace, filterRes);
nsIntRect preFilterVisualOverflowRect;
if (aPreFilterVisualOverflowRectOverride) {
preFilterVisualOverflowRect =
MapFrameRectToFilterSpace(aPreFilterVisualOverflowRectOverride,
appUnitsPerCSSPx,
frameSpaceInCSSPxTofilterSpace, filterRes);
} else {
nsRect preFilterVOR = aTarget->GetPreEffectsVisualOverflowRect();
preFilterVisualOverflowRect =
MapFrameRectToFilterSpace(&preFilterVOR, appUnitsPerCSSPx,
frameSpaceInCSSPxTofilterSpace, filterRes);
}
nsIntRect targetBoundsFilterSpace =
MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, &targetBoundsDeviceSpace);
// Setup instance data
mInstance = new nsSVGFilterInstance(aTarget, aPaint, filter, bbox, filterRegion,
nsIntSize(filterRes.width, filterRes.height),
filterToDeviceSpace, targetBoundsFilterSpace,
postFilterDirtyRect, preFilterDirtyRect,
primitiveUnits);
mInstance =
new nsSVGFilterInstance(aTarget, aPaint, filter, bbox, filterRegion,
nsIntSize(filterRes.width, filterRes.height),
filterToDeviceSpace, filterToFrameSpaceInCSSPx,
preFilterVisualOverflowRect, postFilterDirtyRect,
preFilterDirtyRect, primitiveUnits);
}
PRUint16
@ -388,13 +432,13 @@ nsresult
nsSVGFilterFrame::PaintFilteredFrame(nsRenderingContext *aContext,
nsIFrame *aFilteredFrame,
nsSVGFilterPaintCallback *aPaintCallback,
const nsIntRect *aDirtyArea)
const nsRect *aDirtyArea)
{
nsAutoFilterInstance instance(aFilteredFrame, this, aPaintCallback,
aDirtyArea, nsnull, nsnull);
if (!instance.get())
if (!instance.get()) {
return NS_OK;
}
nsRefPtr<gfxASurface> result;
nsresult rv = instance.get()->Render(getter_AddRefs(result));
if (NS_SUCCEEDED(rv) && result) {
@ -404,118 +448,75 @@ 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)
static nsRect
TransformFilterSpaceToFrameSpace(nsSVGFilterInstance *aInstance,
nsIntRect *aRect)
{
gfxMatrix m = aInstance->GetFilterSpaceToDeviceSpaceTransform();
gfxMatrix m = aInstance->GetFilterSpaceToFrameSpaceInCSSPxTransform();
gfxRect r(aRect->x, aRect->y, aRect->width, aRect->height);
r = m.TransformBounds(r);
r.RoundOut();
nsIntRect deviceRect;
if (!gfxUtils::GfxRectToIntRect(r, &deviceRect))
return NS_ERROR_FAILURE;
*aRect = deviceRect;
return NS_OK;
return nsLayoutUtils::RoundGfxRectToAppRect(r, aInstance->AppUnitsPerCSSPixel());
}
nsIntRect
nsRect
nsSVGFilterFrame::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
const nsIntRect& aPreFilterDirtyRect)
const nsRect& 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,
overrideCTM ? &ctm : nsnull);
if (!instance.get())
return nsIntRect();
&aPreFilterDirtyRect, nsnull);
if (!instance.get()) {
return nsRect();
}
// 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()->ComputePostFilterDirtyRect(&dirtyRect);
if (NS_SUCCEEDED(rv)) {
rv = TransformFilterSpaceToDeviceSpace(instance.get(), &dirtyRect);
if (NS_SUCCEEDED(rv))
return dirtyRect;
return TransformFilterSpaceToFrameSpace(instance.get(), &dirtyRect);
}
return nsIntRect();
return nsRect();
}
nsIntRect
nsRect
nsSVGFilterFrame::GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
const nsIntRect& aPostFilterDirtyRect)
const nsRect& aPostFilterDirtyRect)
{
nsAutoFilterInstance instance(aFilteredFrame, this, nsnull,
&aPostFilterDirtyRect, nsnull, nsnull);
if (!instance.get())
return nsIntRect();
if (!instance.get()) {
return nsRect();
}
// 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 TransformFilterSpaceToFrameSpace(instance.get(), &neededRect);
}
return nsIntRect();
return nsRect();
}
nsIntRect
nsRect
nsSVGFilterFrame::GetPostFilterBounds(nsIFrame *aFilteredFrame,
const gfxRect *aOverrideBBox,
const nsIntRect *aPreFilterBounds)
const nsRect *aPreFilterBounds)
{
bool overrideCTM = false;
gfxMatrix ctm;
if (aFilteredFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
// For most filter operations on SVG frames we want information in
// outer-<svg> device space, but in this case we want the visual overflow
// rect relative to aTarget itself. For that we need to prevent the filter
// code using GetCanvasTM().
overrideCTM = true;
ctm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFilteredFrame);
}
nsAutoFilterInstance instance(aFilteredFrame, this, nsnull, nsnull,
aPreFilterBounds, aOverrideBBox,
overrideCTM ? &ctm : nsnull);
if (!instance.get())
return nsIntRect();
aPreFilterBounds, aPreFilterBounds,
aOverrideBBox);
if (!instance.get()) {
return nsRect();
}
// 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 TransformFilterSpaceToFrameSpace(instance.get(), &bbox);
}
return nsIntRect();
return nsRect();
}
#ifdef DEBUG

View File

@ -45,41 +45,46 @@ public:
nsIAtom* aAttribute,
PRInt32 aModType);
/**
* Paint the given filtered frame.
* @param aDirtyArea The area than needs to be painted, in aFilteredFrame's
* frame space (i.e. relative to its origin, the top-left corner of its
* border box).
*/
nsresult PaintFilteredFrame(nsRenderingContext *aContext,
nsIFrame *aFilteredFrame,
nsSVGFilterPaintCallback *aPaintCallback,
const nsIntRect* aDirtyArea);
const nsRect* aDirtyArea);
/**
* Returns the post-filter area that could be dirtied when the given
* pre-filter area of aFilteredFrame changes. The rects are in device pixels,
* relative to the origin of the outer-<svg> if aFilteredFrame is SVG, or
* else relative to aFilteredFrame itself.
* pre-filter area of aFilteredFrame changes.
* @param aPreFilterDirtyRect The pre-filter area of aFilteredFrame that has
* changed, relative to aFilteredFrame, in app units.
*/
nsIntRect GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
const nsIntRect& aPreFilterDirtyRect);
nsRect GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
const nsRect& aPreFilterDirtyRect);
/**
* Returns the pre-filter area that is needed from aFilteredFrame when the
* given post-filter area needs to be repainted. The rects are in device
* pixels, relative to the origin of the outer-<svg> if aFilteredFrame is
* SVG, or else relative to aFilteredFrame itself.
* given post-filter area needs to be repainted.
* @param aPostFilterDirtyRect The post-filter area that is dirty, relative
* to aFilteredFrame, in app units.
*/
nsIntRect GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
const nsIntRect& aPostFilterDirtyRect);
nsRect GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
const nsRect& aPostFilterDirtyRect);
/**
* Returns the post-filter paint bounds of aFilteredFrame. The rects are
* relative to the origin of the outer-<svg> if aFilteredFrame is SVG, or
* else relative to aFilteredFrame itself.
* @param aOverrideBBox A user space rect that should be used as
* aFilteredFrame's bbox, if non-null.
* Returns the post-filter visual overflow rect (paint bounds) of
* aFilteredFrame.
* @param aOverrideBBox A user space rect, in user units, that should be used
* as aFilteredFrame's bbox ('bbox' is a specific SVG term), if non-null.
* @param aPreFilterBounds The pre-filter visual overflow rect of
* aFilteredFrame in device pixels, if non-null.
* aFilteredFrame, if non-null.
*/
nsIntRect GetPostFilterBounds(nsIFrame *aFilteredFrame,
const gfxRect *aOverrideBBox = nsnull,
const nsIntRect *aPreFilterBounds = nsnull);
nsRect GetPostFilterBounds(nsIFrame *aFilteredFrame,
const gfxRect *aOverrideBBox = nsnull,
const nsRect *aPreFilterBounds = nsnull);
#ifdef DEBUG
NS_IMETHOD Init(nsIContent* aContent,

View File

@ -78,6 +78,7 @@ public:
const gfxRect& aFilterRegion,
const nsIntSize& aFilterSpaceSize,
const gfxMatrix &aFilterSpaceToDeviceSpaceTransform,
const gfxMatrix &aFilterSpaceToFrameSpaceInCSSPxTransform,
const nsIntRect& aTargetBounds,
const nsIntRect& aPostFilterDirtyRect,
const nsIntRect& aPreFilterDirtyRect,
@ -87,6 +88,7 @@ public:
mFilterElement(aFilterElement),
mTargetBBox(aTargetBBox),
mFilterSpaceToDeviceSpaceTransform(aFilterSpaceToDeviceSpaceTransform),
mFilterSpaceToFrameSpaceInCSSPxTransform(aFilterSpaceToFrameSpaceInCSSPxTransform),
mFilterRegion(aFilterRegion),
mFilterSpaceSize(aFilterSpaceSize),
mSurfaceRect(nsIntPoint(0, 0), aFilterSpaceSize),
@ -192,6 +194,21 @@ public:
gfxPoint FilterSpaceToUserSpace(const gfxPoint& aPt) const;
/**
* Returns the transform from filter space to frame space, in CSS px. This
* transform does not transform to frame space in its normal app units, since
* app units are ints, requiring appropriate rounding which can't be done by
* a transform matrix. Callers have to do that themselves as appropriate for
* their needs.
*/
gfxMatrix GetFilterSpaceToFrameSpaceInCSSPxTransform() const {
return mFilterSpaceToFrameSpaceInCSSPxTransform;
}
PRInt32 AppUnitsPerCSSPixel() const {
return mTargetFrame->PresContext()->AppUnitsPerCSSPixel();
}
private:
typedef nsSVGFE::Image Image;
typedef nsSVGFE::ColorModel ColorModel;
@ -364,6 +381,7 @@ private:
gfxRect mTargetBBox;
gfxMatrix mFilterSpaceToDeviceSpaceTransform;
gfxMatrix mFilterSpaceToFrameSpaceInCSSPxTransform;
gfxRect mFilterRegion;
nsIntSize mFilterSpaceSize;
nsIntRect mSurfaceRect;

View File

@ -260,8 +260,7 @@ nsRect
overrideBBox.RoundOut();
nsRect overflowRect =
filterFrame->GetPostFilterBounds(firstFrame, &overrideBBox).
ToAppUnits(aFrame->PresContext()->AppUnitsPerDevPixel());
filterFrame->GetPostFilterBounds(firstFrame, &overrideBBox);
// Return overflowRect relative to aFrame, rather than "user space":
return overflowRect - (aFrame->GetOffsetTo(firstFrame) + firstFrameToUserSpace);
@ -293,18 +292,14 @@ nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame,
return aFrame->GetVisualOverflowRect();
}
// Convert aInvalidRect into "user space" in dev pixels:
PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
// Convert aInvalidRect into "user space" in app units:
nsPoint toUserSpace =
aFrame->GetOffsetTo(firstFrame) + GetOffsetToUserSpace(firstFrame);
nsIntRect preEffectsRect =
(aInvalidRect + toUserSpace).ToOutsidePixels(appUnitsPerDevPixel);
nsRect preEffectsRect = aInvalidRect + toUserSpace;
nsIntRect postEffectsRect =
filterFrame->GetPostFilterDirtyArea(firstFrame, preEffectsRect);
// Return result relative to aFrame, rather than "user space":
return postEffectsRect.ToAppUnits(appUnitsPerDevPixel) - toUserSpace;
// Return ther result, relative to aFrame, not in user space:
return filterFrame->GetPostFilterDirtyArea(firstFrame, preEffectsRect) -
toUserSpace;
}
nsRect
@ -320,18 +315,14 @@ nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
if (!filterFrame)
return aDirtyRect;
// Convert aDirtyRect into "user space" in dev pixels:
PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
// Convert aDirtyRect into "user space" in app units:
nsPoint toUserSpace =
aFrame->GetOffsetTo(firstFrame) + GetOffsetToUserSpace(firstFrame);
nsIntRect postEffectsRect =
(aDirtyRect + toUserSpace).ToOutsidePixels(appUnitsPerDevPixel);
nsRect postEffectsRect = aDirtyRect + toUserSpace;
nsIntRect preEffectsRect =
filterFrame->GetPreFilterNeededArea(firstFrame, postEffectsRect);
// Return result relative to aFrame, rather than "user space":
return preEffectsRect.ToAppUnits(appUnitsPerDevPixel) - toUserSpace;
// Return ther result, relative to aFrame, not in user space:
return filterFrame->GetPreFilterNeededArea(firstFrame, postEffectsRect) -
toUserSpace;
}
bool
@ -453,8 +444,7 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx,
if (filterFrame) {
RegularFramePaintCallback callback(aBuilder, aInnerList, aEffectsFrame,
offset);
nsIntRect dirtyRect = (aDirtyRect - offset)
.ToOutsidePixels(appUnitsPerDevPixel);
nsRect dirtyRect = aDirtyRect - offset;
filterFrame->PaintFilteredFrame(aCtx, aEffectsFrame, &callback, &dirtyRect);
} else {
gfx->SetMatrix(matrixAutoSaveRestore.Matrix());

View File

@ -591,12 +591,8 @@ nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
return aPreFilterRect;
}
PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
nsIntRect preFilterRect =
aPreFilterRect.ToOutsidePixels(appUnitsPerDevPixel);
nsIntRect rect = filter->GetPostFilterBounds(aFrame, nsnull, &preFilterRect);
nsRect r = rect.ToAppUnits(appUnitsPerDevPixel) - aFrame->GetPosition();
return r;
return filter->GetPostFilterBounds(aFrame, nsnull, &aPreFilterRect) -
aFrame->GetPosition();
}
bool
@ -658,7 +654,6 @@ nsSVGUtils::InvalidateBounds(nsIFrame *aFrame, bool aDuringUpdate,
aFrame = aFrame->GetParent();
}
PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
PRInt32 appUnitsPerCSSPx = aFrame->PresContext()->AppUnitsPerCSSPixel();
while (aFrame) {
@ -689,9 +684,7 @@ nsSVGUtils::InvalidateBounds(nsIFrame *aFrame, bool aDuringUpdate,
nsSVGFilterFrame *filterFrame = nsSVGEffects::GetFilterFrame(aFrame);
if (filterFrame) {
invalidArea =
filterFrame->GetPostFilterDirtyArea(aFrame,
invalidArea.ToOutsidePixels(appUnitsPerDevPixel)).
ToAppUnits(appUnitsPerDevPixel);
filterFrame->GetPostFilterDirtyArea(aFrame, invalidArea);
}
if (aFrame->IsTransformed()) {
invalidArea =
@ -1041,6 +1034,21 @@ nsSVGUtils::GetCanvasTM(nsIFrame *aFrame)
return static_cast<nsSVGGeometryFrame*>(aFrame)->GetCanvasTM();
}
gfxMatrix
nsSVGUtils::GetUserToCanvasTM(nsIFrame *aFrame)
{
nsISVGChildFrame* svgFrame = do_QueryFrame(aFrame);
NS_ASSERTION(svgFrame, "bad frame");
gfxMatrix tm;
if (svgFrame) {
nsSVGElement *content = static_cast<nsSVGElement*>(aFrame->GetContent());
tm = content->PrependLocalTransformsTo(GetCanvasTM(aFrame->GetParent()),
nsSVGElement::eUserSpaceToParent);
}
return tm;
}
void
nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame *aFrame, PRUint32 aFlags)
{
@ -1218,9 +1226,28 @@ nsSVGUtils::PaintFrameWithEffects(nsRenderingContext *aContext,
/* Paint the child */
if (filterFrame) {
nsRect* dirtyRect = nsnull;
nsRect tmpDirtyRect;
if (aDirtyRect) {
// aDirtyRect is in outer-<svg> device pixels, but the filter code needs
// it in frame space.
gfxMatrix userToDeviceSpace = GetUserToCanvasTM(aFrame);
if (userToDeviceSpace.IsSingular()) {
return;
}
gfxMatrix deviceToUserSpace = userToDeviceSpace;
deviceToUserSpace.Invert();
gfxRect dirtyBounds = deviceToUserSpace.TransformBounds(
gfxRect(aDirtyRect->x, aDirtyRect->y,
aDirtyRect->width, aDirtyRect->height));
tmpDirtyRect =
nsLayoutUtils::RoundGfxRectToAppRect(
dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
aFrame->GetPosition();
dirtyRect = &tmpDirtyRect;
}
SVGPaintCallback paintCallback;
filterFrame->PaintFilteredFrame(aContext, aFrame, &paintCallback,
aDirtyRect);
filterFrame->PaintFilteredFrame(aContext, aFrame, &paintCallback, dirtyRect);
} else {
svgChildFrame->PaintSVG(aContext, aDirtyRect);
}

View File

@ -472,6 +472,17 @@ public:
*/
static gfxMatrix GetCanvasTM(nsIFrame* aFrame);
/**
* Returns the transform from aFrame's user space to canvas space. Only call
* with SVG frames. This is like GetCanvasTM, except that it only includes
* the transforms from aFrame's user space (i.e. the coordinate context
* established by its 'transform' attribute, or else the coordinate context
* that its _parent_ establishes for its children) to outer-<svg> device
* space. Specifically, it does not include any other transforms introduced
* by the frame such as x/y offsets and viewBox attributes.
*/
static gfxMatrix GetUserToCanvasTM(nsIFrame* aFrame);
/**
* Notify the descendants of aFrame of a change to one of their ancestors
* that might affect them.