--- a/content/svg/content/src/nsSVGFilters.cpp
+++ b/content/svg/content/src/nsSVGFilters.cpp
@@ -567,23 +567,25 @@ AreAllColorChannelsZero(const nsSVGFE::I
{
return aTarget->mConstantColorChannels &&
aTarget->mImage->GetDataSize() >= 4 &&
(*reinterpret_cast<PRUint32*>(aTarget->mImage->Data()) & 0x00FFFFFF) == 0;
}
void
nsSVGFEGaussianBlurElement::GaussianBlur(const Image *aSource,
- const Image *aTarget, const nsIntRect& aDataRect,
+ const Image *aTarget,
+ const nsIntRect& aDataRect,
PRUint32 aDX, PRUint32 aDY)
{
NS_ASSERTION(nsIntRect(0,0,aTarget->mImage->Width(),aTarget->mImage->Height()).Contains(aDataRect),
"aDataRect out of bounds");
- nsAutoArrayPtr<PRUint8> tmp(new PRUint8[aTarget->mImage->GetDataSize()]); if (!tmp)
+ nsAutoArrayPtr<PRUint8> tmp(new PRUint8[aTarget->mImage->GetDataSize()]);
+ if (!tmp)
return;
memset(tmp, 0, aTarget->mImage->GetDataSize());
PRBool alphaOnly = AreAllColorChannelsZero(aTarget);
const PRUint8* sourceData = aSource->mImage->Data();
PRUint8* targetData = aTarget->mImage->Data();
PRUint32 stride = aTarget->mImage->Stride();
@@ -610,46 +612,102 @@ nsSVGFEGaussianBlurElement::GaussianBlur
PRInt32 ms = major*4;
BoxBlur(tmp + ms, targetData + ms, stride, aDataRect.y, aDataRect.YMost(), longLobe, shortLobe, alphaOnly);
BoxBlur(targetData + ms, tmp + ms, stride, aDataRect.y, aDataRect.YMost(), shortLobe, longLobe, alphaOnly);
BoxBlur(tmp + ms, targetData + ms, stride, aDataRect.y, aDataRect.YMost(), longLobe, longLobe, alphaOnly);
}
}
}
+static void
+InflateRectForBlurDXY(nsIntRect* aRect, PRUint32 aDX, PRUint32 aDY)
+{
+ aRect->Inflate(3*(aDX/2), 3*(aDY/2));
+}
+
+static void
+ClearRect(gfxImageSurface* aSurface, PRInt32 aX, PRInt32 aY,
+ PRInt32 aXMost, PRInt32 aYMost)
+{
+ NS_ASSERTION(aX <= aXMost && aY <= aYMost, "Invalid rectangle");
+ NS_ASSERTION(aX >= 0 && aY >= 0 && aXMost <= aSurface->Width() && aYMost <= aSurface->Height(),
+ "Rectangle out of bounds");
+
+ if (aX == aXMost || aY == aYMost)
+ return;
+ for (PRInt32 y = aY; y < aYMost; ++y) {
+ memset(aSurface->Data() + aSurface->Stride()*y + aX*4, 0, (aXMost - aX)*4);
+ }
+}
+
+// Clip aTarget's image to its filter primitive subregion.
+// aModifiedRect contains all the pixels which might not be RGBA(0,0,0,0),
+// it's relative to the surface data.
+static void
+ClipTarget(nsSVGFilterInstance* aInstance, const nsSVGFE::Image* aTarget,
+ const nsIntRect& aModifiedRect)
+{
+ nsIntPoint surfaceTopLeft = aInstance->GetSurfaceRect().TopLeft();
+
+ NS_ASSERTION(aInstance->GetSurfaceRect().Contains(aModifiedRect + surfaceTopLeft),
+ "Modified data area overflows the surface?");
+
+ nsIntRect clip = aModifiedRect;
+ nsSVGUtils::ClipToGfxRect(&clip,
+ aTarget->mFilterPrimitiveSubregion - gfxPoint(surfaceTopLeft.x, surfaceTopLeft.y));
+
+ ClearRect(aTarget->mImage, aModifiedRect.x, aModifiedRect.y, aModifiedRect.XMost(), clip.y);
+ ClearRect(aTarget->mImage, aModifiedRect.x, clip.y, clip.x, clip.YMost());
+ ClearRect(aTarget->mImage, clip.XMost(), clip.y, aModifiedRect.XMost(), clip.YMost());
+ ClearRect(aTarget->mImage, aModifiedRect.x, clip.YMost(), aModifiedRect.XMost(), aModifiedRect.YMost());
+}
+
+static void
+ClipComputationRectToSurface(nsSVGFilterInstance* aInstance,
+ nsIntRect* aDataRect)
+{
+ aDataRect->IntersectRect(*aDataRect,
+ nsRect(nsPoint(0, 0), aInstance->GetSurfaceRect().Size()));
+}
+
nsresult
nsSVGFEGaussianBlurElement::Filter(nsSVGFilterInstance* aInstance,
const nsTArray<const Image*>& aSources,
const Image* aTarget,
const nsIntRect& rect)
{
PRUint32 dx, dy;
nsresult rv = GetDXY(&dx, &dy, *aInstance);
if (rv == NS_ERROR_UNEXPECTED) // zero std deviation
return NS_OK;
if (NS_FAILED(rv))
return rv;
- GaussianBlur(aSources[0], aTarget, rect, dx, dy);
+
+ nsIntRect computationRect = rect;
+ InflateRectForBlurDXY(&computationRect, dx, dy);
+ ClipComputationRectToSurface(aInstance, &computationRect);
+ GaussianBlur(aSources[0], aTarget, computationRect, dx, dy);
+ ClipTarget(aInstance, aTarget, computationRect);
return NS_OK;
}
void
nsSVGFEGaussianBlurElement::GetSourceImageNames(nsTArray<nsSVGString*>* aSources)
{
aSources->AppendElement(&mStringAttributes[IN1]);
}
void
nsSVGFEGaussianBlurElement::InflateRectForBlur(nsRect* aRect,
const nsSVGFilterInstance& aInstance)
{
PRUint32 dX, dY;
nsresult rv = GetDXY(&dX, &dY, aInstance);
if (NS_SUCCEEDED(rv)) {
- aRect->Inflate(3*(dX/2), 3*(dY/2));
+ InflateRectForBlurDXY(aRect, dX, dY);
}
}
nsRect
nsSVGFEGaussianBlurElement::ComputeTargetBBox(const nsTArray<nsRect>& aSourceBBoxes,
const nsSVGFilterInstance& aInstance)
{
nsRect r = aSourceBBoxes[0];
@@ -2762,16 +2820,18 @@ nsSVGFETileElement::Filter(nsSVGFilterIn
rect.x, rect.y, rect.width, rect.height);
#endif
// XXX This code depends on the surface rect containing the filter
// primitive subregion. ComputeTargetBBox, ComputeNeededSourceBBoxes
// and ComputeChangeBBox are all pessimal, so that will normally be OK,
// but nothing clips mFilterPrimitiveSubregion so this should be changed.
const gfxRect& tileGfx = aSources[0]->mFilterPrimitiveSubregion;
+ // XXX this is bad, technically the filter primitive subregion could be
+ // out of PRInt32 bounds
nsIntRect tile(PRInt32(tileGfx.X()), PRInt32(tileGfx.Y()),
PRInt32(tileGfx.Width()), PRInt32(tileGfx.Height()));
if (tile.IsEmpty())
return NS_OK;
NS_ASSERTION(instance->GetSurfaceRect().Contains(tile),
"Tile overflows surface rect, this code can't handle it");
// Get it into surface space
tile -= instance->GetSurfaceRect().TopLeft();
@@ -3554,18 +3614,18 @@ nsSVGFEMorphologyElement::Filter(nsSVGFi
* Thus, we can avoid iterating over the entire kernel by comparing the
* leading edge of the new kernel against the extrema found in the previous
* kernel. We must still scan the entire kernel if the previous extrema do
* not fall within the current kernel or if we are starting a new row.
*/
for (PRInt32 y = rect.y; y < rect.YMost(); y++) {
PRUint32 startY = PR_MAX(0, y - ry);
// We need to read pixels not just in 'rect', which is limited to
- // our filter primitive subregion, but all pixels in the given radii
- // from the source surface, so use fr.GetHeight/fr.GetWidth here.
+ // the dirty part of our filter primitive subregion, but all pixels in
+ // the given radii from the source surface, so use the surface size here.
PRUint32 endY = PR_MIN(y + ry, instance->GetSurfaceHeight() - 1);
for (PRInt32 x = rect.x; x < rect.XMost(); x++) {
PRUint32 startX = PR_MAX(0, x - rx);
PRUint32 endX = PR_MIN(x + rx, instance->GetSurfaceWidth() - 1);
PRUint32 targIndex = y * stride + 4 * x;
// We need to scan the entire kernel
if (x == rect.x || xExt[0] <= startX || xExt[1] <= startX ||
@@ -4533,17 +4593,18 @@ Convolve3x3(const PRUint8 *index, PRInt3
if (k)
sum += k * index[4 * (x - 1) + stride * (y - 1)];
}
}
return sum;
}
static void
-GenerateNormal(float *N, const PRUint8 *data, PRInt32 stride, nsRect rect,
+GenerateNormal(float *N, const PRUint8 *data, PRInt32 stride,
+ PRInt32 surfaceWidth, PRInt32 surfaceHeight,
PRInt32 x, PRInt32 y, float surfaceScale)
{
// See this for source of constants:
// http://www.w3.org/TR/SVG11/filters.html#feDiffuseLighting
static const PRInt8 Kx[3][3][3][3] =
{ { { { 0, 0, 0}, { 0, -2, 2}, { 0, -1, 1} },
{ { 0, 0, 0}, {-2, 0, 2}, {-1, 0, 1} },
{ { 0, 0, 0}, {-2, 2, 0}, {-1, 1, 0} } },
@@ -4570,24 +4631,24 @@ GenerateNormal(float *N, const PRUint8 *
static const float FACTORy[3][3] =
{ { 2.0 / 3.0, 1.0 / 2.0, 2.0 / 3.0 },
{ 1.0 / 3.0, 1.0 / 4.0, 1.0 / 3.0 },
{ 2.0 / 3.0, 1.0 / 2.0, 2.0 / 3.0 } };
PRInt8 xflag, yflag;
if (x == 0) {
xflag = 0;
- } else if (x == rect.width - 1) {
+ } else if (x == surfaceWidth - 1) {
xflag = 2;
} else {
xflag = 1;
}
if (y == 0) {
yflag = 0;
- } else if (y == rect.height - 1) {
+ } else if (y == surfaceHeight - 1) {
yflag = 2;
} else {
yflag = 1;
}
const PRUint8 *index = data + y * stride + 4 * x + GFX_ARGB32_OFFSET_A;
N[0] = -surfaceScale * FACTORx[yflag][xflag] *
@@ -4676,23 +4737,26 @@ nsSVGFELightingElement::Filter(nsSVGFilt
}
float surfaceScale = mNumberAttributes[SURFACE_SCALE].GetAnimValue();
const nsIntRect& dataRect = info.mDataRect;
PRInt32 stride = info.mSource->Stride();
PRUint8 *sourceData = info.mSource->Data();
PRUint8 *targetData = info.mTarget->Data();
-
+ PRInt32 surfaceWidth = info.mSource->Width();
+ PRInt32 surfaceHeight = info.mSource->Height();
+
for (PRInt32 y = dataRect.y; y < dataRect.YMost(); y++) {
for (PRInt32 x = dataRect.x; x < dataRect.XMost(); x++) {
PRInt32 index = y * stride + x * 4;
float N[3];
- GenerateNormal(N, sourceData, stride, rect, x, y, surfaceScale);
+ GenerateNormal(N, sourceData, stride, surfaceWidth, surfaceHeight,
+ x, y, surfaceScale);
if (pointLight || spotLight) {
float Z =
surfaceScale * sourceData[index + GFX_ARGB32_OFFSET_A] / 255;
L[0] = lightPos[0] - x;
L[1] = lightPos[1] - y;
L[2] = lightPos[2] - Z;
@@ -5520,18 +5584,18 @@ nsSVGFEDisplacementMapElement::Filter(ns
float scale = mNumberAttributes[SCALE].GetAnimValue();
if (scale == 0.0f) {
CopyRect(aTarget, aSources[0], rect);
return NS_OK;
}
NS_ASSERTION(instance->GetSurfaceRect().Size() == instance->GetFilterSpaceSize(),
"Surface size optimization should have been disabled, see ComputeNeededSourceBBoxes");
- PRInt32 width = instance->GetSurfaceRect().width;
- PRInt32 height = instance->GetSurfaceRect().height;
+ PRInt32 width = instance->GetSurfaceWidth();
+ PRInt32 height = instance->GetSurfaceHeight();
PRUint8* sourceData = aSources[0]->mImage->Data();
PRUint8* displacementData = aSources[1]->mImage->Data();
PRUint8* targetData = aTarget->mImage->Data();
PRUint32 stride = aTarget->mImage->Stride();
nsSVGLength2 val;
val.Init(nsSVGUtils::XY, 0xff, scale, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER);
--- a/content/svg/content/src/nsSVGFilters.h
+++ b/content/svg/content/src/nsSVGFilters.h
@@ -159,19 +159,20 @@ public:
nsTArray<nsIntRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance);
// Perform the actual filter operation.
// We guarantee that every mImage from aSources and aTarget has the
// same width, height, stride and device offset.
// aTarget is already filled in. This function just needs to fill in the
// pixels of aTarget->mImage (which have already been cleared).
// @param aDataRect the destination rectangle that needs to be painted,
- // relative to aTarget's surface data. Output must be clipped to this
- // rectangle. This is the intersection of the filter primitive subregion
- // for this filter element and the temporary surface area.
+ // relative to aTarget's surface data. This is the intersection of the
+ // filter primitive subregion for this filter element and the
+ // temporary surface area. Output need not be clipped to this rect but
+ // it must be clipped to aTarget->mFilterPrimitiveSubregion.
virtual nsresult Filter(nsSVGFilterInstance* aInstance,
const nsTArray<const Image*>& aSources,
const Image* aTarget,
const nsIntRect& aDataRect) = 0;
static nsIntRect GetMaxRect() {
// Try to avoid overflow errors dealing with this rect. It will
// be intersected with some other reasonable-sized rect eventually.
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/feGaussianBlur-3-ref.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
+
+<rect x="100" y="100" width="100" height="100" fill="#00ff00"/>
+
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/feGaussianBlur-3.svg
@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
+
+<filter id="f1" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse">
+ <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur"/>
+ <feFlood flood-opacity="0" flood-color="black"/>
+ <feComposite in="blur" operator="over" x="100" y="100" width="100" height="100"/>
+</filter>
+<g filter="url(#f1)">
+ <rect x="100" y="100" width="100" height="100" fill="#00ff00"/>
+</g>
+
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/feGaussianBlur-4-ref.svg
@@ -0,0 +1,10 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
+<filter id="f1" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse">
+ <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur"/>
+ <feFlood flood-opacity="0" flood-color="black"/>
+ <feComposite in="blur" operator="over" x="100" y="100" width="100" height="100"/>
+</filter>
+<g filter="url(#f1)">
+ <rect x="100" y="100" width="100" height="100" fill="#00ff00"/>
+</g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/feGaussianBlur-4.svg
@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
+<filter id="f1" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse">
+ <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" x="100" y="100" width="100" height="100"/>
+</filter>
+<g filter="url(#f1)">
+ <rect x="100" y="100" width="100" height="100" fill="#00ff00"/>
+</g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/feGaussianBlur-5-ref.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
+<g>
+ <rect x="100" y="100" width="100" height="100" fill="#00ff00"/>
+</g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/feGaussianBlur-5.svg
@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
+<filter id="f1" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse">
+ <feGaussianBlur stdDeviation="0.0001"/>
+</filter>
+<g filter="url(#f1)">
+ <rect x="100" y="100" width="100" height="100" fill="#00ff00"/>
+</g>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/filters/feGaussianBlur-6.svg
@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500">
+<filter id="f1" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse">
+ <feGaussianBlur stdDeviation="0"/>
+</filter>
+<g filter="url(#f1)">
+ <rect x="100" y="100" width="100" height="100" fill="#00ff00"/>
+</g>
+</svg>
--- a/layout/reftests/svg/filters/reftest.list
+++ b/layout/reftests/svg/filters/reftest.list
@@ -20,16 +20,20 @@
== feDisplacementMap-1.svg feDisplacementMap-1-ref.svg
== feDisplacementMap-2.svg feDisplacementMap-2-ref.svg
== feFlood-1.svg feFlood-1-ref.svg
== feFlood-2.svg feFlood-2-ref.svg
== feGaussianBlur-1.svg feGaussianBlur-1-ref.svg
== feGaussianBlur-2.svg feGaussianBlur-2-ref.svg
+!= feGaussianBlur-3.svg feGaussianBlur-3-ref.svg
+== feGaussianBlur-4.svg feGaussianBlur-4-ref.svg
+== feGaussianBlur-5.svg feGaussianBlur-5-ref.svg
+== feGaussianBlur-6.svg about:blank
== feImage-1.svg feImage-1-ref.svg
== feMerge-1.svg feMerge-1-ref.svg
== feMerge-2.svg feMerge-2-ref.svg
== feMorphology-1.svg feMorphology-1-ref.svg
== feMorphology-2.svg feMorphology-2-ref.svg
--- a/layout/svg/base/src/nsSVGFilterFrame.cpp
+++ b/layout/svg/base/src/nsSVGFilterFrame.cpp
@@ -80,17 +80,18 @@ nsSVGFilterFrame::FilterFailCleanup(nsSV
aTarget->SetMatrixPropagation(PR_TRUE);
aTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
nsISVGChildFrame::TRANSFORM_CHANGED);
aTarget->PaintSVG(aContext, nsnull);
}
nsresult
nsSVGFilterFrame::FilterPaint(nsSVGRenderState *aContext,
- nsISVGChildFrame *aTarget)
+ nsISVGChildFrame *aTarget,
+ const nsRect *aDirtyRect)
{
nsCOMPtr<nsIDOMSVGFilterElement> aFilter = do_QueryInterface(mContent);
NS_ASSERTION(aFilter, "Wrong content element (not filter)");
nsIFrame *frame;
CallQueryInterface(aTarget, &frame);
nsCOMPtr<nsIDOMSVGMatrix> ctm = nsSVGUtils::GetCanvasTM(frame);
@@ -178,40 +179,55 @@ nsSVGFilterFrame::FilterPaint(nsSVGRende
NS_NewSVGMatrix(getter_AddRefs(filterTransform),
filterRes.width / width, 0.0f,
0.0f, filterRes.height / height,
-x * filterRes.width / width, -y * filterRes.height / height);
aTarget->SetOverrideCTM(filterTransform);
aTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
nsISVGChildFrame::TRANSFORM_CHANGED);
+ // 'fini' is the matrix we will finally use to transform filter space
+ // to surface space for drawing
+ nsCOMPtr<nsIDOMSVGMatrix> scale, fini;
+ NS_NewSVGMatrix(getter_AddRefs(scale),
+ width / filterRes.width, 0.0f,
+ 0.0f, height / filterRes.height,
+ x, y);
+ ctm->Multiply(scale, getter_AddRefs(fini));
+
+ nsIntRect dirtyRect(0, 0, filterRes.width, filterRes.height);
+ if (aDirtyRect) {
+ gfxMatrix finiM = nsSVGUtils::ConvertSVGMatrixToThebes(fini);
+ // fini is always invertible.
+ finiM.Invert();
+ gfxRect r = finiM.TransformBounds(gfxRect(aDirtyRect->x, aDirtyRect->y,
+ aDirtyRect->width, aDirtyRect->height));
+ r.RoundOut();
+ nsIntRect intRect;
+ if (NS_SUCCEEDED(nsSVGUtils::GfxRectToIntRect(r, &intRect))) {
+ dirtyRect = intRect;
+ }
+ }
+
// Setup instance data
PRUint16 primitiveUnits =
filter->mEnumAttributes[nsSVGFilterElement::PRIMITIVEUNITS].GetAnimValue();
nsSVGFilterInstance instance(aTarget, mContent, bbox,
gfxRect(x, y, width, height),
nsIntSize(filterRes.width, filterRes.height),
- primitiveUnits);
+ dirtyRect, primitiveUnits);
nsRefPtr<gfxASurface> result;
nsresult rv = instance.Render(getter_AddRefs(result));
if (NS_FAILED(rv)) {
FilterFailCleanup(aContext, aTarget);
return NS_OK;
}
if (result) {
- nsCOMPtr<nsIDOMSVGMatrix> scale, fini;
- NS_NewSVGMatrix(getter_AddRefs(scale),
- width / filterRes.width, 0.0f,
- 0.0f, height / filterRes.height,
- x, y);
-
- ctm->Multiply(scale, getter_AddRefs(fini));
-
nsSVGUtils::CompositeSurfaceMatrix(aContext->GetGfxContext(),
result, fini, 1.0);
}
aTarget->SetOverrideCTM(nsnull);
aTarget->SetMatrixPropagation(PR_TRUE);
aTarget->NotifySVGChanged(nsISVGChildFrame::SUPPRESS_INVALIDATION |
nsISVGChildFrame::TRANSFORM_CHANGED);
@@ -271,16 +287,19 @@ nsSVGFilterFrame::GetInvalidationRegion(
height = nsSVGUtils::UserSpace(targetContent, tmpHeight);
}
#ifdef DEBUG_tor
fprintf(stderr, "invalidate box: %f,%f %fx%f\n", x, y, width, height);
#endif
// transform back
+ // XXXroc this should use nsSVGUtils::ConvertSVGMatrixToThebes
+ // and gfxMatrix::TransformBounds and gfxRect::RoundOut and
+ // nsSVGUtils::GfxRectToIntRect
float xx[4], yy[4];
xx[0] = x; yy[0] = y;
xx[1] = x + width; yy[1] = y;
xx[2] = x + width; yy[2] = y + height;
xx[3] = x; yy[3] = y + height;
nsSVGUtils::TransformPoint(ctm, &xx[0], &yy[0]);
nsSVGUtils::TransformPoint(ctm, &xx[1], &yy[1]);
--- a/layout/svg/base/src/nsSVGFilterFrame.h
+++ b/layout/svg/base/src/nsSVGFilterFrame.h
@@ -47,17 +47,18 @@ class nsSVGFilterFrame : public nsSVGFil
{
friend nsIFrame*
NS_NewSVGFilterFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsStyleContext* aContext);
protected:
nsSVGFilterFrame(nsStyleContext* aContext) : nsSVGFilterFrameBase(aContext) {}
public:
nsresult FilterPaint(nsSVGRenderState *aContext,
- nsISVGChildFrame *aTarget);
+ nsISVGChildFrame *aTarget,
+ const nsRect* aDirtyRect);
// Returns invalidation region for filter (can be bigger than the
// referencing geometry to filter region sizing) in device pixels
// relative to the origin of the outer svg.
nsRect GetInvalidationRegion(nsIFrame *aTarget);
/**
* Get the "type" of the frame
--- a/layout/svg/base/src/nsSVGFilterInstance.cpp
+++ b/layout/svg/base/src/nsSVGFilterInstance.cpp
@@ -267,19 +267,18 @@ nsSVGFilterInstance::ComputeResultBoundi
void
nsSVGFilterInstance::ComputeNeededBoxes()
{
if (mPrimitives.IsEmpty())
return;
// In the end, we need whatever the final filter primitive will draw.
- // XXX we could optimize this by intersecting with the dirty rect here!!!
- mPrimitives[mPrimitives.Length() - 1].mResultNeededBox
- = mPrimitives[mPrimitives.Length() - 1].mResultBoundingBox;
+ mPrimitives[mPrimitives.Length() - 1].mResultNeededBox.IntersectRect(
+ mPrimitives[mPrimitives.Length() - 1].mResultBoundingBox, mDirtyRect);
for (PRInt32 i = mPrimitives.Length() - 1; i >= 0; --i) {
PrimitiveInfo* info = &mPrimitives[i];
nsAutoTArray<nsIntRect,2> sourceBBoxes;
for (PRUint32 j = 0; j < info->mInputs.Length(); ++j) {
sourceBBoxes.AppendElement(info->mInputs[j]->mResultBoundingBox);
}
--- a/layout/svg/base/src/nsSVGFilterInstance.h
+++ b/layout/svg/base/src/nsSVGFilterInstance.h
@@ -64,22 +64,24 @@ class NS_STACK_CLASS nsSVGFilterInstance
public:
float GetPrimitiveLength(nsSVGLength2 *aLength) const;
nsSVGFilterInstance(nsISVGChildFrame *aTargetFrame,
nsIContent* aFilterElement,
nsIDOMSVGRect *aTargetBBox,
const gfxRect& aFilterRect,
const nsIntSize& aFilterSpaceSize,
+ const nsIntRect& aDirtyRect,
PRUint16 aPrimitiveUnits) :
mTargetFrame(aTargetFrame),
mFilterElement(aFilterElement),
mTargetBBox(aTargetBBox),
mFilterRect(aFilterRect),
mFilterSpaceSize(aFilterSpaceSize),
+ mDirtyRect(aDirtyRect),
mSurfaceRect(nsIntPoint(0, 0), aFilterSpaceSize),
mPrimitiveUnits(aPrimitiveUnits) {
}
// The area covered by temporary images, in filter space
void SetSurfaceRect(const nsIntRect& aRect) { mSurfaceRect = aRect; }
gfxRect GetFilterRect() const { return mFilterRect; }
@@ -157,16 +159,17 @@ private:
return static_cast<nsSVGElement*>(f->GetContent());
}
nsISVGChildFrame* mTargetFrame;
nsIContent* mFilterElement;
nsCOMPtr<nsIDOMSVGRect> mTargetBBox;
gfxRect mFilterRect;
nsIntSize mFilterSpaceSize;
+ nsIntRect mDirtyRect;
nsIntRect mSurfaceRect;
PRUint16 mPrimitiveUnits;
PrimitiveInfo mSourceColorAlpha;
PrimitiveInfo mSourceAlpha;
nsTArray<PrimitiveInfo> mPrimitives;
};
--- a/layout/svg/base/src/nsSVGUtils.cpp
+++ b/layout/svg/base/src/nsSVGUtils.cpp
@@ -1370,17 +1370,17 @@ nsSVGUtils::PaintChildWithEffects(nsSVGR
if (clipPathFrame && isTrivialClip) {
gfx->Save();
clipPathFrame->ClipPaint(aContext, svgChildFrame, matrix);
}
/* Paint the child */
nsSVGFilterFrame *filterFrame = GetFilterFrame(state, aFrame);
if (filterFrame) {
- filterFrame->FilterPaint(aContext, svgChildFrame);
+ filterFrame->FilterPaint(aContext, svgChildFrame, aDirtyRect);
} else {
svgChildFrame->PaintSVG(aContext, aDirtyRect);
}
if (clipPathFrame && isTrivialClip) {
gfx->Restore();
}