mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Bug 767823 - Make nsAutoFilterInstance take frame rects and stop using GetCoveredRegion. r=longsonr.
This commit is contained in:
parent
7b8dafc300
commit
f970cf9626
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user