author | Jonathan Watt <jwatt@jwatt.org> |
Fri, 13 Apr 2012 14:22:06 +0100 | |
changeset 91595 | dc0b57deb507d4685fe1fb8656c1f7d5ea5551fd |
parent 91594 | c44c5a7141e3ccbbb03d314e1ac59255ec9176d8 |
child 91596 | c0975fb1e62dc57e436f2f6aa3781433feeabe52 |
push id | 8288 |
push user | jwatt@jwatt.org |
push date | Fri, 13 Apr 2012 13:24:39 +0000 |
treeherder | mozilla-inbound@dc0b57deb507 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | longsonr |
bugs | 745066 |
milestone | 14.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/layout/svg/base/src/nsSVGFilterFrame.cpp +++ b/layout/svg/base/src/nsSVGFilterFrame.cpp @@ -95,38 +95,33 @@ private: class NS_STACK_CLASS nsAutoFilterInstance { public: nsAutoFilterInstance(nsIFrame *aTarget, nsSVGFilterFrame *aFilterFrame, nsSVGFilterPaintCallback *aPaint, 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". nsSVGFilterInstance* get() { return mInstance; } private: nsAutoPtr<nsSVGFilterInstance> 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, nsSVGFilterFrame *aFilterFrame, nsSVGFilterPaintCallback *aPaint, const nsIntRect *aDirtyOutputRect, const nsIntRect *aDirtyInputRect, const nsIntRect *aOverrideSourceBBox) { - mTarget = do_QueryFrame(aTarget); - const nsSVGFilterElement *filter = aFilterFrame->GetFilterContent(); PRUint16 filterUnits = aFilterFrame->GetEnumValue(nsSVGFilterElement::FILTERUNITS); PRUint16 primitiveUnits = aFilterFrame->GetEnumValue(nsSVGFilterElement::PRIMITIVEUNITS); gfxRect bbox; @@ -242,20 +237,16 @@ nsAutoFilterInstance::nsAutoFilterInstan // Setup instance data mInstance = new nsSVGFilterInstance(aTarget, aPaint, filter, bbox, filterRegion, nsIntSize(filterRes.width, filterRes.height), filterToDeviceSpace, targetBoundsFilterSpace, dirtyOutputRect, dirtyInputRect, primitiveUnits); } -nsAutoFilterInstance::~nsAutoFilterInstance() -{ -} - PRUint16 nsSVGFilterFrame::GetEnumValue(PRUint32 aIndex, nsIContent *aDefault) { nsSVGEnum& thisEnum = static_cast<nsSVGFilterElement *>(mContent)->mEnumAttributes[aIndex]; if (thisEnum.IsExplicitlySet()) return thisEnum.GetAnimValue();
--- a/layout/svg/base/src/nsSVGFilterInstance.h +++ b/layout/svg/base/src/nsSVGFilterInstance.h @@ -57,20 +57,52 @@ class nsSVGFilterElement; class nsSVGFilterPaintCallback; /** * This class performs all filter processing. * * 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-<svg> 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, const gfxRect &aTargetBBox, const gfxRect& aFilterRect, const nsIntSize& aFilterSpaceSize, const gfxMatrix &aFilterSpaceToDeviceSpaceTransform, const nsIntRect& aTargetBounds, @@ -79,81 +111,168 @@ public: PRUint16 aPrimitiveUnits) : mTargetFrame(aTargetFrame), mPaintCallback(aPaintCallback), mFilterElement(aFilterElement), mTargetBBox(aTargetBBox), 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 { return GetPrimitiveNumber(aCtxType, aNumber->GetAnimValue()); } float GetPrimitiveNumber(PRUint8 aCtxType, const nsSVGNumberPair *aNumberPair, nsSVGNumberPair::PairIndex aIndex) const { 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-<svg> device space. + */ gfxMatrix GetFilterSpaceToDeviceSpaceTransform() const { return mFilterSpaceToDeviceSpaceTransform; } + gfxPoint FilterSpaceToUserSpace(const gfxPoint& aPt) const; private: typedef nsSVGFE::Image Image; 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 // live in nsTArrays themselves and nsTArray moves the elements // around in memory, which breaks nsAutoTArray. nsTArray<PrimitiveInfo*> mInputs; PrimitiveInfo() : mFE(nsnull), mImageUsers(0) {} @@ -163,67 +282,148 @@ private: public: ImageAnalysisEntry(KeyTypePointer aStr) : nsStringHashKey(aStr) { } ImageAnalysisEntry(const ImageAnalysisEntry& toCopy) : nsStringHashKey(toCopy), mInfo(toCopy.mInfo) { } 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<gfxImageSurface> 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); /** * Scales a numeric filter primitive length in the X, Y or "XY" directions * into a length in filter space (no offset is applied). */ 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 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-<svg> 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-<svg> 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; - nsIntRect mSurfaceRect; + + /** + * The 'primitiveUnits' attribute value (objectBoundingBox or userSpaceOnUse). + */ PRUint16 mPrimitiveUnits; PrimitiveInfo mSourceColorAlpha; PrimitiveInfo mSourceAlpha; nsTArray<PrimitiveInfo> mPrimitives; }; #endif
--- a/layout/svg/base/src/nsSVGUtils.h +++ b/layout/svg/base/src/nsSVGUtils.h @@ -537,17 +537,19 @@ public: }; /** * Get the SVG bbox (the SVG spec's simplified idea of bounds) of aFrame in * aFrame's userspace. */ 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; * may be null if aUnits is not SVG_UNIT_TYPE_OBJECTBOUNDINGBOX * @param aFrame the object in which to interpret user-space units; * may be null if aUnits is SVG_UNIT_TYPE_OBJECTBOUNDINGBOX */ static gfxRect