diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 48f119d1c0d..523d05c614b 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -5001,6 +5001,14 @@ nsIFrame::GetVisualOverflowRectRelativeToSelf() const return GetVisualOverflowRect(); } +nsRect +nsIFrame::GetPreEffectsVisualOverflowRect() const +{ + nsRect* r = static_cast + (Properties().Get(nsIFrame::PreEffectsBBoxProperty())); + return r ? *r : GetVisualOverflowRectRelativeToSelf(); +} + /* virtual */ bool nsFrame::UpdateOverflow() { diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 08629a7c5de..b79d01872aa 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -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 diff --git a/layout/svg/base/src/nsSVGFilterFrame.cpp b/layout/svg/base/src/nsSVGFilterFrame.cpp index a044b22a51a..3063553141b 100644 --- a/layout/svg/base/src/nsSVGFilterFrame.cpp +++ b/layout/svg/base/src/nsSVGFilterFrame.cpp @@ -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(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- 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 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- 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 diff --git a/layout/svg/base/src/nsSVGFilterFrame.h b/layout/svg/base/src/nsSVGFilterFrame.h index c5ae6dd2b0e..edc0543e526 100644 --- a/layout/svg/base/src/nsSVGFilterFrame.h +++ b/layout/svg/base/src/nsSVGFilterFrame.h @@ -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- 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- 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- 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, diff --git a/layout/svg/base/src/nsSVGFilterInstance.h b/layout/svg/base/src/nsSVGFilterInstance.h index e899c93c15d..262e8afb8a2 100644 --- a/layout/svg/base/src/nsSVGFilterInstance.h +++ b/layout/svg/base/src/nsSVGFilterInstance.h @@ -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; diff --git a/layout/svg/base/src/nsSVGIntegrationUtils.cpp b/layout/svg/base/src/nsSVGIntegrationUtils.cpp index d439ec11f40..cc1cfb29621 100644 --- a/layout/svg/base/src/nsSVGIntegrationUtils.cpp +++ b/layout/svg/base/src/nsSVGIntegrationUtils.cpp @@ -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()); diff --git a/layout/svg/base/src/nsSVGUtils.cpp b/layout/svg/base/src/nsSVGUtils.cpp index f8687e6d12e..309edd9ebb7 100644 --- a/layout/svg/base/src/nsSVGUtils.cpp +++ b/layout/svg/base/src/nsSVGUtils.cpp @@ -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(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(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- 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); } diff --git a/layout/svg/base/src/nsSVGUtils.h b/layout/svg/base/src/nsSVGUtils.h index 184da264844..afc986b41b5 100644 --- a/layout/svg/base/src/nsSVGUtils.h +++ b/layout/svg/base/src/nsSVGUtils.h @@ -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- 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.