Bug 948265 - [CSS Filters] Make nsSVGIntegrationUtils and nsSVGUtils call nsSVGFilterInstance directly instead of through nsSVGFilterFrame. r=roc

This commit is contained in:
Max Vujovic 2014-02-07 10:48:39 -05:00
parent be875d9a91
commit 142f7f4250
6 changed files with 188 additions and 161 deletions

View File

@ -203,101 +203,6 @@ nsSVGFilterFrame::AttributeChanged(int32_t aNameSpaceID,
aAttribute, aModType);
}
nsresult
nsSVGFilterFrame::PaintFilteredFrame(nsRenderingContext *aContext,
nsIFrame *aFilteredFrame,
nsSVGFilterPaintCallback *aPaintCallback,
const nsRect *aDirtyArea,
nsIFrame* aTransformRoot)
{
nsSVGFilterInstance instance(aFilteredFrame, this, aPaintCallback,
aDirtyArea, nullptr, nullptr, nullptr,
aTransformRoot);
if (!instance.IsInitialized()) {
return NS_OK;
}
return instance.Render(aContext->ThebesContext());
}
static nsRect
TransformFilterSpaceToFrameSpace(nsSVGFilterInstance *aInstance,
nsIntRect *aRect)
{
if (aRect->IsEmpty()) {
return nsRect();
}
gfxMatrix m = aInstance->GetFilterSpaceToFrameSpaceInCSSPxTransform();
gfxRect r(aRect->x, aRect->y, aRect->width, aRect->height);
r = m.TransformBounds(r);
return nsLayoutUtils::RoundGfxRectToAppRect(r, aInstance->AppUnitsPerCSSPixel());
}
nsRect
nsSVGFilterFrame::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
const nsRect& aPreFilterDirtyRect)
{
if (aPreFilterDirtyRect.IsEmpty()) {
return nsRect();
}
nsSVGFilterInstance instance(aFilteredFrame, this, nullptr, nullptr,
&aPreFilterDirtyRect);
if (!instance.IsInitialized()) {
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.ComputePostFilterDirtyRect(&dirtyRect);
if (NS_SUCCEEDED(rv)) {
return TransformFilterSpaceToFrameSpace(&instance, &dirtyRect);
}
return nsRect();
}
nsRect
nsSVGFilterFrame::GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
const nsRect& aPostFilterDirtyRect)
{
nsSVGFilterInstance instance(aFilteredFrame, this, nullptr,
&aPostFilterDirtyRect);
if (!instance.IsInitialized()) {
return nsRect();
}
// Now we can ask the instance to compute the area of the source
// that's needed.
nsIntRect neededRect;
nsresult rv = instance.ComputeSourceNeededRect(&neededRect);
if (NS_SUCCEEDED(rv)) {
return TransformFilterSpaceToFrameSpace(&instance, &neededRect);
}
return nsRect();
}
nsRect
nsSVGFilterFrame::GetPostFilterBounds(nsIFrame *aFilteredFrame,
const gfxRect *aOverrideBBox,
const nsRect *aPreFilterBounds)
{
MOZ_ASSERT(!(aFilteredFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
!(aFilteredFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
"Non-display SVG do not maintain visual overflow rects");
nsSVGFilterInstance instance(aFilteredFrame, this, nullptr, nullptr,
aPreFilterBounds, aPreFilterBounds,
aOverrideBBox);
if (!instance.IsInitialized()) {
return nsRect();
}
nsIntRect bbox;
nsresult rv = instance.ComputePostFilterExtents(&bbox);
if (NS_SUCCEEDED(rv)) {
return TransformFilterSpaceToFrameSpace(&instance, &bbox);
}
return nsRect();
}
#ifdef DEBUG
void
nsSVGFilterFrame::Init(nsIContent* aContent,

View File

@ -57,48 +57,6 @@ public:
nsIAtom* aAttribute,
int32_t aModType) MOZ_OVERRIDE;
/**
* 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 nsRect* aDirtyArea,
nsIFrame* aTransformRoot);
/**
* Returns the post-filter area that could be dirtied when the given
* pre-filter area of aFilteredFrame changes.
* @param aPreFilterDirtyRect The pre-filter area of aFilteredFrame that has
* changed, relative to aFilteredFrame, in app units.
*/
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.
* @param aPostFilterDirtyRect The post-filter area that is dirty, relative
* to aFilteredFrame, in app units.
*/
nsRect GetPreFilterNeededArea(nsIFrame *aFilteredFrame,
const nsRect& aPostFilterDirtyRect);
/**
* 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, if non-null.
*/
nsRect GetPostFilterBounds(nsIFrame *aFilteredFrame,
const gfxRect *aOverrideBBox = nullptr,
const nsRect *aPreFilterBounds = nullptr);
#ifdef DEBUG
virtual void Init(nsIContent* aContent,
nsIFrame* aParent,

View File

@ -23,6 +23,105 @@ using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::gfx;
nsresult
nsSVGFilterInstance::PaintFilteredFrame(nsSVGFilterFrame* aFilterFrame,
nsRenderingContext *aContext,
nsIFrame *aFilteredFrame,
nsSVGFilterPaintCallback *aPaintCallback,
const nsRect *aDirtyArea,
nsIFrame* aTransformRoot)
{
nsSVGFilterInstance instance(aFilteredFrame, aFilterFrame, aPaintCallback,
aDirtyArea, nullptr, nullptr, nullptr,
aTransformRoot);
if (!instance.IsInitialized()) {
return NS_OK;
}
return instance.Render(aContext->ThebesContext());
}
static nsRect
TransformFilterSpaceToFrameSpace(nsSVGFilterInstance *aInstance,
nsIntRect *aRect)
{
if (aRect->IsEmpty()) {
return nsRect();
}
gfxMatrix m = aInstance->GetFilterSpaceToFrameSpaceInCSSPxTransform();
gfxRect r(aRect->x, aRect->y, aRect->width, aRect->height);
r = m.TransformBounds(r);
return nsLayoutUtils::RoundGfxRectToAppRect(r, aInstance->AppUnitsPerCSSPixel());
}
nsRect
nsSVGFilterInstance::GetPostFilterDirtyArea(nsSVGFilterFrame* aFilterFrame,
nsIFrame *aFilteredFrame,
const nsRect& aPreFilterDirtyRect)
{
if (aPreFilterDirtyRect.IsEmpty()) {
return nsRect();
}
nsSVGFilterInstance instance(aFilteredFrame, aFilterFrame, nullptr, nullptr,
&aPreFilterDirtyRect);
if (!instance.IsInitialized()) {
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.
nsRect dirtyRect;
nsresult rv = instance.ComputePostFilterDirtyRect(&dirtyRect);
if (NS_SUCCEEDED(rv)) {
return dirtyRect;
}
return nsRect();
}
nsRect
nsSVGFilterInstance::GetPreFilterNeededArea(nsSVGFilterFrame* aFilterFrame,
nsIFrame *aFilteredFrame,
const nsRect& aPostFilterDirtyRect)
{
nsSVGFilterInstance instance(aFilteredFrame, aFilterFrame, nullptr,
&aPostFilterDirtyRect);
if (!instance.IsInitialized()) {
return nsRect();
}
// Now we can ask the instance to compute the area of the source
// that's needed.
nsRect neededRect;
nsresult rv = instance.ComputeSourceNeededRect(&neededRect);
if (NS_SUCCEEDED(rv)) {
return neededRect;
}
return nsRect();
}
nsRect
nsSVGFilterInstance::GetPostFilterBounds(nsSVGFilterFrame* aFilterFrame,
nsIFrame *aFilteredFrame,
const gfxRect *aOverrideBBox,
const nsRect *aPreFilterBounds)
{
MOZ_ASSERT(!(aFilteredFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
!(aFilteredFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
"Non-display SVG do not maintain visual overflow rects");
nsSVGFilterInstance instance(aFilteredFrame, aFilterFrame, nullptr, nullptr,
aPreFilterBounds, aPreFilterBounds,
aOverrideBBox);
if (!instance.IsInitialized()) {
return nsRect();
}
nsRect bbox;
nsresult rv = instance.ComputePostFilterExtents(&bbox);
if (NS_SUCCEEDED(rv)) {
return bbox;
}
return nsRect();
}
nsSVGFilterInstance::nsSVGFilterInstance(nsIFrame *aTargetFrame,
nsSVGFilterFrame *aFilterFrame,
nsSVGFilterPaintCallback *aPaintCallback,
@ -664,9 +763,9 @@ nsSVGFilterInstance::Render(gfxContext* aContext)
}
nsresult
nsSVGFilterInstance::ComputePostFilterDirtyRect(nsIntRect* aPostFilterDirtyRect)
nsSVGFilterInstance::ComputePostFilterDirtyRect(nsRect* aPostFilterDirtyRect)
{
*aPostFilterDirtyRect = nsIntRect();
*aPostFilterDirtyRect = nsRect();
if (mPreFilterDirtyRect.IsEmpty()) {
return NS_OK;
}
@ -685,14 +784,15 @@ nsSVGFilterInstance::ComputePostFilterDirtyRect(nsIntRect* aPostFilterDirtyRect)
nsIntRegion resultChangeRegion =
FilterSupport::ComputeResultChangeRegion(filter,
mPreFilterDirtyRect, nsIntRegion(), nsIntRegion());
*aPostFilterDirtyRect = resultChangeRegion.GetBounds();
*aPostFilterDirtyRect =
FilterSpaceToFrameSpace(resultChangeRegion.GetBounds());
return NS_OK;
}
nsresult
nsSVGFilterInstance::ComputePostFilterExtents(nsIntRect* aPostFilterExtents)
nsSVGFilterInstance::ComputePostFilterExtents(nsRect* aPostFilterExtents)
{
*aPostFilterExtents = nsIntRect();
*aPostFilterExtents = nsRect();
nsresult rv = BuildPrimitives();
if (NS_FAILED(rv))
@ -714,12 +814,12 @@ nsSVGFilterInstance::ComputePostFilterExtents(nsIntRect* aPostFilterExtents)
FilterDescription filter(mPrimitiveDescriptions, filterSpaceBounds);
nsIntRegion postFilterExtents =
FilterSupport::ComputePostFilterExtents(filter, sourceBoundsInt);
*aPostFilterExtents = postFilterExtents.GetBounds();
*aPostFilterExtents = FilterSpaceToFrameSpace(postFilterExtents.GetBounds());
return NS_OK;
}
nsresult
nsSVGFilterInstance::ComputeSourceNeededRect(nsIntRect* aDirty)
nsSVGFilterInstance::ComputeSourceNeededRect(nsRect* aDirty)
{
nsresult rv = BuildPrimitives();
if (NS_FAILED(rv))
@ -731,7 +831,7 @@ nsSVGFilterInstance::ComputeSourceNeededRect(nsIntRect* aDirty)
}
ComputeNeededBoxes();
*aDirty = mSourceGraphic.mNeededBounds;
*aDirty = FilterSpaceToFrameSpace(mSourceGraphic.mNeededBounds);
return NS_OK;
}
@ -757,6 +857,17 @@ nsSVGFilterInstance::FrameSpaceToFilterSpace(const nsRect* aRect) const
return rect;
}
nsRect
nsSVGFilterInstance::FilterSpaceToFrameSpace(const nsIntRect& aRect) const
{
if (aRect.IsEmpty()) {
return nsRect();
}
gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
r = mFilterSpaceToFrameSpaceInCSSPxTransform.TransformBounds(r);
return nsLayoutUtils::RoundGfxRectToAppRect(r, mAppUnitsPerCSSPx);
}
gfxMatrix
nsSVGFilterInstance::GetUserSpaceToFrameSpaceInCSSPxTransform() const
{

View File

@ -59,6 +59,52 @@ class nsSVGFilterInstance
typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription;
public:
/**
* 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).
*/
static nsresult PaintFilteredFrame(nsSVGFilterFrame* aFilterFrame,
nsRenderingContext *aContext,
nsIFrame *aFilteredFrame,
nsSVGFilterPaintCallback *aPaintCallback,
const nsRect* aDirtyArea,
nsIFrame* aTransformRoot = nullptr);
/**
* Returns the post-filter area that could be dirtied when the given
* pre-filter area of aFilteredFrame changes.
* @param aPreFilterDirtyRect The pre-filter area of aFilteredFrame that has
* changed, relative to aFilteredFrame, in app units.
*/
static nsRect GetPostFilterDirtyArea(nsSVGFilterFrame* aFilterFrame,
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.
* @param aPostFilterDirtyRect The post-filter area that is dirty, relative
* to aFilteredFrame, in app units.
*/
static nsRect GetPreFilterNeededArea(nsSVGFilterFrame* aFilterFrame,
nsIFrame *aFilteredFrame,
const nsRect& aPostFilterDirtyRect);
/**
* 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, if non-null.
*/
static nsRect GetPostFilterBounds(nsSVGFilterFrame* aFilterFrame,
nsIFrame *aFilteredFrame,
const gfxRect *aOverrideBBox = nullptr,
const nsRect *aPreFilterBounds = nullptr);
/**
* @param aTargetFrame The frame of the filtered element under consideration.
* @param aFilterFrame The frame of the SVG filter element.
@ -118,31 +164,31 @@ public:
nsresult Render(gfxContext* aContext);
/**
* Sets the aPostFilterDirtyRect outparam to the post-filter bounds in filter
* Sets the aPostFilterDirtyRect outparam to the post-filter bounds in frame
* space of the area that would be dirtied by mTargetFrame when a given
* pre-filter area of mTargetFrame is dirtied. The pre-filter area must have
* been specified before calling this method by passing it as the
* aPreFilterDirtyRect argument to the nsSVGFilterInstance constructor.
*/
nsresult ComputePostFilterDirtyRect(nsIntRect* aPostFilterDirtyRect);
nsresult ComputePostFilterDirtyRect(nsRect* aPostFilterDirtyRect);
/**
* Sets the aPostFilterExtents outparam to the post-filter bounds in filter
* Sets the aPostFilterExtents outparam to the post-filter bounds in frame
* space for the whole filter output. This is not necessarily equivalent to
* the area that would be dirtied in the result when the entire pre-filter
* area is dirtied, because some filter primitives can generate output
* without any input.
*/
nsresult ComputePostFilterExtents(nsIntRect* aPostFilterExtents);
nsresult ComputePostFilterExtents(nsRect* aPostFilterExtents);
/**
* Sets the aDirty outparam to the pre-filter bounds in filter space of the
* Sets the aDirty outparam to the pre-filter bounds in frame space of the
* area of mTargetFrame that is needed in order to paint the filtered output
* for a given post-filter dirtied area. The post-filter area must have been
* specified before calling this method by passing it as the aPostFilterDirtyRect
* argument to the nsSVGFilterInstance constructor.
*/
nsresult ComputeSourceNeededRect(nsIntRect* aDirty);
nsresult ComputeSourceNeededRect(nsRect* aDirty);
float GetPrimitiveNumber(uint8_t aCtxType, const nsSVGNumber2 *aNumber) const
{
@ -269,6 +315,7 @@ private:
* large to be stored in an nsIntRect.
*/
nsIntRect FrameSpaceToFilterSpace(const nsRect* aRect) const;
nsRect FilterSpaceToFrameSpace(const nsIntRect& aRect) const;
/**
* Returns the transform from frame space to the coordinate space that

View File

@ -16,6 +16,7 @@
#include "nsSVGEffects.h"
#include "nsSVGElement.h"
#include "nsSVGFilterFrame.h"
#include "nsSVGFilterInstance.h"
#include "nsSVGFilterPaintCallback.h"
#include "nsSVGMaskFrame.h"
#include "nsSVGPaintServerFrame.h"
@ -276,7 +277,7 @@ nsRect
overrideBBox.RoundOut();
nsRect overflowRect =
filterFrame->GetPostFilterBounds(firstFrame, &overrideBBox);
nsSVGFilterInstance::GetPostFilterBounds(filterFrame, firstFrame, &overrideBBox);
// Return overflowRect relative to aFrame, rather than "user space":
return overflowRect - (aFrame->GetOffsetTo(firstFrame) + firstFrameToUserSpace);
@ -327,8 +328,8 @@ nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame,
// Adjust the dirty area for effects, and shift it back to being relative to
// the reference frame.
nsRect result = filterFrame->GetPostFilterDirtyArea(firstFrame, preEffectsRect) -
toUserSpace;
nsRect result = nsSVGFilterInstance::GetPostFilterDirtyArea(filterFrame,
firstFrame, preEffectsRect) - toUserSpace;
// Return the result, in pixels relative to the reference frame.
return result.ToOutsidePixels(appUnitsPerDevPixel);
}
@ -352,8 +353,8 @@ nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
nsRect postEffectsRect = aDirtyRect + toUserSpace;
// Return ther result, relative to aFrame, not in user space:
return filterFrame->GetPreFilterNeededArea(firstFrame, postEffectsRect) -
toUserSpace;
return nsSVGFilterInstance::GetPreFilterNeededArea(filterFrame, firstFrame,
postEffectsRect) - toUserSpace;
}
bool
@ -517,7 +518,8 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx,
RegularFramePaintCallback callback(aBuilder, aLayerManager,
offsetWithoutSVGGeomFramePos);
nsRect dirtyRect = aDirtyRect - offset;
filterFrame->PaintFilteredFrame(aCtx, aFrame, &callback, &dirtyRect, nullptr);
nsSVGFilterInstance::PaintFilteredFrame(filterFrame, aCtx, aFrame,
&callback, &dirtyRect);
} else {
gfx->SetMatrix(matrixAutoSaveRestore.Matrix());
aLayerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder);

View File

@ -36,6 +36,7 @@
#include "nsSVGContainerFrame.h"
#include "nsSVGEffects.h"
#include "nsSVGFilterFrame.h"
#include "nsSVGFilterInstance.h"
#include "nsSVGFilterPaintCallback.h"
#include "nsSVGForeignObjectFrame.h"
#include "gfxSVGGlyphs.h"
@ -159,12 +160,13 @@ nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
NS_ABORT_IF_FALSE(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT,
"Called on invalid frame type");
nsSVGFilterFrame *filter = nsSVGEffects::GetFilterFrame(aFrame);
if (!filter) {
nsSVGFilterFrame *filterFrame = nsSVGEffects::GetFilterFrame(aFrame);
if (!filterFrame) {
return aPreFilterRect;
}
return filter->GetPostFilterBounds(aFrame, nullptr, &aPreFilterRect);
return nsSVGFilterInstance::GetPostFilterBounds(filterFrame, aFrame, nullptr,
&aPreFilterRect);
}
bool
@ -621,7 +623,9 @@ nsSVGUtils::PaintFrameWithEffects(nsRenderingContext *aContext,
dirtyRect = &tmpDirtyRect;
}
SVGPaintCallback paintCallback;
filterFrame->PaintFilteredFrame(aContext, aFrame, &paintCallback, dirtyRect, aTransformRoot);
nsSVGFilterInstance::PaintFilteredFrame(filterFrame, aContext, aFrame,
&paintCallback, dirtyRect,
aTransformRoot);
} else {
svgChildFrame->PaintSVG(aContext, aDirtyRect, aTransformRoot);
}