diff --git a/layout/svg/base/src/nsSVGFilterFrame.cpp b/layout/svg/base/src/nsSVGFilterFrame.cpp index 737692739664..98c7e81e3341 100644 --- a/layout/svg/base/src/nsSVGFilterFrame.cpp +++ b/layout/svg/base/src/nsSVGFilterFrame.cpp @@ -100,7 +100,7 @@ public: const nsIntRect *aDirtyOutputRect, const nsIntRect *aDirtyInputRect, const nsIntRect *aOverrideSourceBBox); - ~nsAutoFilterInstance(); + ~nsAutoFilterInstance() {} // If this returns null, then draw nothing. Either the filter draws // nothing or it is "in error". @@ -108,9 +108,6 @@ public: private: nsAutoPtr mInstance; - // Store mTarget separately even though mInstance has it, because if - // mInstance creation fails we still need to be able to clean up - nsISVGChildFrame* mTarget; }; nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget, @@ -120,8 +117,6 @@ nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget, const nsIntRect *aDirtyInputRect, const nsIntRect *aOverrideSourceBBox) { - mTarget = do_QueryFrame(aTarget); - const nsSVGFilterElement *filter = aFilterFrame->GetFilterContent(); PRUint16 filterUnits = @@ -247,10 +242,6 @@ nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget, primitiveUnits); } -nsAutoFilterInstance::~nsAutoFilterInstance() -{ -} - PRUint16 nsSVGFilterFrame::GetEnumValue(PRUint32 aIndex, nsIContent *aDefault) { diff --git a/layout/svg/base/src/nsSVGFilterInstance.h b/layout/svg/base/src/nsSVGFilterInstance.h index 565a86a72c6c..4834f36d88d0 100644 --- a/layout/svg/base/src/nsSVGFilterInstance.h +++ b/layout/svg/base/src/nsSVGFilterInstance.h @@ -62,10 +62,42 @@ class nsSVGFilterPaintCallback; * We build a graph of the filter image data flow, essentially * converting the filter graph to SSA. This lets us easily propagate * analysis data (such as bounding-boxes) over the filter primitive graph. + * + * Definition of "filter space": filter space is a coordinate system that is + * aligned with the user space of the filtered element, with its origin located + * at the top left of the filter region (as returned by GetFilterRect, + * specifically), and with one unit equal in size to one pixel of the offscreen + * surface into which the filter output would/will be painted. */ class NS_STACK_CLASS nsSVGFilterInstance { public: + /** + * @param aTargetFrame The frame of the filtered element under consideration. + * @param aPaintCallback [optional] The callback that Render() should use to + * paint. Only required if you will call Render(). + * @param aFilterElement The filter element referenced by aTargetFrame's + * element. + * @param aTargetBBox The filtered element's bbox, in the filtered element's + * user space. + * @param aFilterRect The "filter region", in the filtered element's user + * space. The caller must have already expanded the region out so that its + * edges coincide with pixel boundaries in the offscreen surface that + * would/will be created to paint the filter output. + * @param aFilterSpaceSize The size of the user specified "filter region", + * in filter space units. + * @param aFilterSpaceToDeviceSpaceTransform The transform from filter + * space to outer- device space. + * @param aTargetBounds The pre-filter paint bounds of the filtered element, + * in filter space. + * @param aDirtyOutputRect [optional] The bounds of the post-filter area that + * has to be repainted, in filter space. Only required if you will call + * ComputeSourceNeededRect() or Render(). + * @param aDirtyInputRect [optional] The bounds of the pre-filter area of the + * filtered element that changed, in filter space. Only required if you + * will call ComputeOutputDirtyRect(). + * @param aPrimitiveUnits The value from the 'primitiveUnits' attribute. + */ nsSVGFilterInstance(nsIFrame *aTargetFrame, nsSVGFilterPaintCallback *aPaintCallback, const nsSVGFilterElement *aFilterElement, @@ -84,29 +116,75 @@ public: mFilterSpaceToDeviceSpaceTransform(aFilterSpaceToDeviceSpaceTransform), mFilterRect(aFilterRect), mFilterSpaceSize(aFilterSpaceSize), + mSurfaceRect(nsIntPoint(0, 0), aFilterSpaceSize), mTargetBounds(aTargetBounds), mDirtyOutputRect(aDirtyOutputRect), mDirtyInputRect(aDirtyInputRect), - mSurfaceRect(nsIntPoint(0, 0), aFilterSpaceSize), mPrimitiveUnits(aPrimitiveUnits) { } - - // The area covered by temporary images, in filter space - void SetSurfaceRect(const nsIntRect& aRect) { mSurfaceRect = aRect; } + /** + * Returns the user specified "filter region", in the filtered element's user + * space, after it has been adjusted out (if necessary) so that its edges + * coincide with pixel boundaries of the offscreen surface into which the + * filtered output would/will be painted. + */ gfxRect GetFilterRect() const { return mFilterRect; } + /** + * Returns the size of the user specified "filter region", in filter space. + * The size will be {filterRes.x by filterRes.y}, whether the user specified + * the filter's filterRes attribute explicitly, or the implementation chose + * the filterRes values. (The top-left of the filter region is the origin of + * filter space, which is why this method returns an nsIntSize and not an + * nsIntRect.) + */ const nsIntSize& GetFilterSpaceSize() { return mFilterSpaceSize; } PRUint32 GetFilterResX() const { return mFilterSpaceSize.width; } PRUint32 GetFilterResY() const { return mFilterSpaceSize.height; } - + + /** + * Returns the dimensions of the offscreen surface that is required for the + * output from the current filter operation, in filter space. This rect is + * clipped to, and therefore guaranteed to be fully contained by, the filter + * region. + */ const nsIntRect& GetSurfaceRect() const { return mSurfaceRect; } PRInt32 GetSurfaceWidth() const { return mSurfaceRect.width; } PRInt32 GetSurfaceHeight() const { return mSurfaceRect.height; } - + + /** + * Allocates a gfxASurface, renders the filtered element into the surface, + * and then returns the surface via the aOutput outparam. The area that + * needs to be painted must have been specified before calling this method + * by passing it as the aDirtyOutputRect argument to the + * nsSVGFilterInstance constructor. + */ nsresult Render(gfxASurface** aOutput); + + /** + * Sets the aDirty outparam to the post-filter bounds in filter 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 aDirtyInputRect argument + * to the nsSVGFilterInstance constructor. + */ nsresult ComputeOutputDirtyRect(nsIntRect* aDirty); + + /** + * Sets the aDirty outparam to the pre-filter bounds in filter 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 aDirtyOutputRect + * argument to the nsSVGFilterInstance constructor. + */ nsresult ComputeSourceNeededRect(nsIntRect* aDirty); + + /** + * Sets the aDirty outparam to the post-filter bounds in filter space of the + * area that would be dirtied by mTargetFrame if its entire pre-filter area + * is dirtied. + */ nsresult ComputeOutputBBox(nsIntRect* aBBox); float GetPrimitiveNumber(PRUint8 aCtxType, const nsSVGNumber2 *aNumber) const @@ -118,17 +196,27 @@ public: { return GetPrimitiveNumber(aCtxType, aNumberPair->GetAnimValue(aIndex)); } + /** - * Converts a point and a length in filter primitive units into filter space. - * For object-bounding-box units, the object bounding box offset is applied - * to the point. + * Converts a userSpaceOnUse/objectBoundingBoxUnits unitless point and length + * into filter space, depending on the value of mPrimitiveUnits. (For + * objectBoundingBoxUnits, the bounding box offset is applied to the point.) */ void ConvertLocation(float aValues[3]) const; + /** + * Returns the transform from the filtered element's user space to filter + * space. This will be a simple translation and/or scale. + */ gfxMatrix GetUserSpaceToFilterSpaceTransform() const; + + /** + * Returns the transform from filter space to outer- device space. + */ gfxMatrix GetFilterSpaceToDeviceSpaceTransform() const { return mFilterSpaceToDeviceSpaceTransform; } + gfxPoint FilterSpaceToUserSpace(const gfxPoint& aPt) const; private: @@ -136,19 +224,50 @@ private: typedef nsSVGFE::ColorModel ColorModel; struct PrimitiveInfo { + /// Pointer to the filter primitive element. nsSVGFE* mFE; - // the bounding box of the result image produced by this primitive, in - // filter space + + /** + * The filter space bounds of this filter primitive's output, were a full + * repaint of mTargetFrame to occur. Note that a filter primitive's output + * (and hence this rect) is always clipped to both the filter region and + * to the filter primitive subregion. + * XXX maybe rename this to mMaxBounds? + */ nsIntRect mResultBoundingBox; - // the bounding box of the part of the result image that is actually - // needed by other primitives or by the filter result, in filter space - // (used for Render only) + + /** + * The filter space bounds of this filter primitive's output, were we to + * repaint a given post-filter dirty area, and were we to minimize + * repainting for that dirty area. In other words this is the part of the + * primitive's output that is needed by other primitives or the final + * filtered output in order to repaint that area. This rect is guaranteed + * to be contained within mResultBoundingBox and, if we're only painting + * part of the filtered output, may be smaller. This rect is used when + * calling Render() or ComputeSourceNeededRect(). + * XXX maybe rename this to just mNeededBounds? + */ nsIntRect mResultNeededBox; - // the bounding box of the part of the result image that could be - // changed by changes to mDirtyInputRect in the source image(s) - // (used for ComputeOutputDirtyRect only) + + /** + * The filter space bounds of this filter primitive's output, were only + * part of mTargetFrame's pre-filter output to be dirtied, and were we to + * minimize repainting for that dirty area. This is used when calculating + * the area that needs to be invalidated when only part of a filtered + * element is dirtied. This rect is guaranteed to be contained within + * mResultBoundingBox. + */ nsIntRect mResultChangeBox; + Image mImage; + + /** + * The number of times that this filter primitive's output is used as an + * input by other filter primitives in the filter graph. + * XXX seems like we could better use this to avoid creating images for + * primitives that are not used, or whose ouput in not used during the + * current operation. + */ PRInt32 mImageUsers; // Can't use nsAutoTArray here, because these Info objects @@ -168,27 +287,76 @@ private: PrimitiveInfo* mInfo; }; + /** + * Initializes the SourceGraphic and SourceAlpha graph nodes (i.e. sets + * .mImage.mFilterPrimitiveSubregion and .mResultBoundingBox on + * mSourceColorAlpha and mSourceAlpha). + */ nsresult BuildSources(); - // Build graph of PrimitiveInfo nodes describing filter primitives - nsresult BuildPrimitives(); - // Compute bounding boxes of the filter primitive outputs - void ComputeResultBoundingBoxes(); - // Compute bounding boxes of what we actually *need* from the filter - // primitive outputs - void ComputeNeededBoxes(); - // Compute bounding boxes of what could have changed given some changes - // to the source images. - void ComputeResultChangeBoxes(); - nsIntRect ComputeUnionOfAllNeededBoxes(); + + /** + * Creates the gfxImageSurfaces for the SourceGraphic and SourceAlpha graph + * nodes, paints their contents, and assigns them to + * mSourceColorAlpha.mImage.mImage and mSourceAlpha.mImage.mImage + * respectively. + */ nsresult BuildSourceImages(); - // Allocates an image surface that covers mSurfaceRect (it uses - // device offsets so that its origin is positioned at mSurfaceRect.TopLeft() - // when using cairo to draw into the surface). The surface is cleared - // to transparent black. + /** + * Build the graph of PrimitiveInfo nodes that describes the filter's filter + * primitives and their connections. This populates mPrimitives, and sets + * each PrimitiveInfo's mFE, mInputs, mImageUsers, mFilterPrimitiveSubregion, + * etc. + */ + nsresult BuildPrimitives(); + + /** + * Compute the filter space bounds of the output from each primitive, were we + * to do a full repaint of mTargetFrame. This sets mResultBoundingBox on the + * items in the filter graph, based on the mResultBoundingBox of each item's + * inputs, and clipped to the filter region and each primitive's filter + * primitive subregion. + */ + void ComputeResultBoundingBoxes(); + + /** + * Computes the filter space bounds of the areas that we actually *need* from + * each filter primitive's output, based on the value of mDirtyOutputRect. + * This sets mResultNeededBox on the items in the filter graph. + */ + void ComputeNeededBoxes(); + + /** + * Computes the filter space bounds of the area of each filter primitive + * that will change, based on the value of mDirtyInputRect. + * This sets mResultChangeBox on the items in the filter graph. + */ + void ComputeResultChangeBoxes(); + + /** + * Computes and returns the union of all mResultNeededBox rects in the filter + * graph. This is useful for deciding the size of the offscreen surface that + * needs to be created for the filter operation. + */ + nsIntRect ComputeUnionOfAllNeededBoxes(); + + /** + * Allocates and returns a surface of mSurfaceRect.Size(), and with a device + * offset of -mSurfaceRect.TopLeft(). The surface is cleared to transparent + * black. + */ already_AddRefed CreateImage(); + /** + * Computes and sets mFilterPrimitiveSubregion for the given primitive. + */ void ComputeFilterPrimitiveSubregion(PrimitiveInfo* aInfo); + + /** + * If the color model of the pixel data in the aPrimitive's image isn't + * already aColorModel, then this method converts its pixel data to that + * color model. + */ void EnsureColorModel(PrimitiveInfo* aPrimitive, ColorModel aColorModel); @@ -199,26 +367,58 @@ private: float GetPrimitiveNumber(PRUint8 aCtxType, float aValue) const; gfxRect UserSpaceToFilterSpace(const gfxRect& aUserSpace) const; + + /** + * Clip the filter space rect aRect to the filter region. + */ void ClipToFilterSpace(nsIntRect* aRect) const { nsIntRect filterSpace(nsIntPoint(0, 0), mFilterSpaceSize); aRect->IntersectRect(*aRect, filterSpace); } - void ClipToGfxRect(nsIntRect* aRect, const gfxRect& aGfx) const; + /** + * The frame for the element that is currently being filtered. + */ nsIFrame* mTargetFrame; + nsSVGFilterPaintCallback* mPaintCallback; const nsSVGFilterElement* mFilterElement; - // Bounding box of the target element, in user space + + /** + * The SVG bbox of the element that is being filtered, in user space. + */ gfxRect mTargetBBox; + gfxMatrix mFilterSpaceToDeviceSpaceTransform; gfxRect mFilterRect; nsIntSize mFilterSpaceSize; - // Filter-space bounds of the target image (SourceAlpha/SourceGraphic) - nsIntRect mTargetBounds; - nsIntRect mDirtyOutputRect; - nsIntRect mDirtyInputRect; nsIntRect mSurfaceRect; + + /** + * Pre-filter paint bounds of the element that is being filtered, in filter + * space. + */ + nsIntRect mTargetBounds; + + /** + * If set, this is the filter space bounds of the outer- device space + * bounds of the dirty area that needs to be repainted. (As bounds-of-bounds, + * this may be a fair bit bigger than we actually need, unfortunately.) + */ + nsIntRect mDirtyOutputRect; + + /** + * If set, this is the filter space bounds of the outer- device bounds + * of the pre-filter area of the filtered element that changed. (As + * bounds-of-bounds, this may be a fair bit bigger than we actually need, + * unfortunately.) + */ + nsIntRect mDirtyInputRect; + + /** + * The 'primitiveUnits' attribute value (objectBoundingBox or userSpaceOnUse). + */ PRUint16 mPrimitiveUnits; PrimitiveInfo mSourceColorAlpha; diff --git a/layout/svg/base/src/nsSVGUtils.h b/layout/svg/base/src/nsSVGUtils.h index 216492b95a07..260739cdadcc 100644 --- a/layout/svg/base/src/nsSVGUtils.h +++ b/layout/svg/base/src/nsSVGUtils.h @@ -542,7 +542,9 @@ public: static gfxRect GetBBox(nsIFrame *aFrame, PRUint32 aFlags = eBBoxIncludeFill); /** - * Compute a rectangle in userSpaceOnUse or objectBoundingBoxUnits. + * Convert a userSpaceOnUse/objectBoundingBoxUnits rectangle that's specified + * using four nsSVGLength2 values into a user unit rectangle in user space. + * * @param aXYWH pointer to 4 consecutive nsSVGLength2 objects containing * the x, y, width and height values in that order * @param aBBox the bounding box of the object the rect is relative to;