/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef LAYOUT_SVG_FILTERINSTANCE_H_ #define LAYOUT_SVG_FILTERINSTANCE_H_ #include "gfxMatrix.h" #include "gfxPoint.h" #include "gfxRect.h" #include "nsCOMPtr.h" #include "FilterDescription.h" #include "nsHashKeys.h" #include "nsPoint.h" #include "nsRect.h" #include "nsSize.h" #include "nsTArray.h" #include "mozilla/gfx/2D.h" #include "mozilla/webrender/WebRenderTypes.h" #include "mozilla/ServoStyleConsts.h" #include "mozilla/SVGIntegrationUtils.h" class gfxContext; class nsIContent; class nsIFrame; struct WrFiltersHolder; namespace mozilla { class SVGFilterFrame; namespace dom { class UserSpaceMetrics; } // namespace dom namespace image { struct imgDrawingParams; } /** * 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, and with one unit equal in size to one * pixel of the offscreen surface into which the filter output would/will be * painted. * * The definition of "filter region" can be found here: * http://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion */ class FilterInstance { using IntRect = gfx::IntRect; using SourceSurface = gfx::SourceSurface; using DrawTarget = gfx::DrawTarget; using FilterPrimitiveDescription = gfx::FilterPrimitiveDescription; using FilterDescription = gfx::FilterDescription; using UserSpaceMetrics = dom::UserSpaceMetrics; using imgDrawingParams = image::imgDrawingParams; using SVGFilterPaintCallback = SVGIntegrationUtils::SVGFilterPaintCallback; public: /** * Create a FilterDescription for the supplied filter. All coordinates in * the description are in filter space. * @param aFilterInputIsTainted Describes whether the SourceImage / * SourceAlpha input is tainted. This affects whether feDisplacementMap * will respect the filter input as its map input, and it affects the * IsTainted() state on the filter primitives in the FilterDescription. * "Tainted" is a term from the filters spec and means security-sensitive * content, i.e. pixels that JS should not be able to read in any way. * @param aOutAdditionalImages Will contain additional images needed to * render the filter (from feImage primitives). * @return A FilterDescription describing the filter. */ static FilterDescription GetFilterDescription( nsIContent* aFilteredElement, Span aFilterChain, nsISupports* aFiltersObserverList, bool aFilterInputIsTainted, const UserSpaceMetrics& aMetrics, const gfxRect& aBBox, nsTArray>& aOutAdditionalImages); /** * 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 void PaintFilteredFrame( nsIFrame* aFilteredFrame, Span aFilterChain, const nsTArray& aFilterFrames, gfxContext* aCtx, const SVGFilterPaintCallback& aPaintCallback, const nsRegion* aDirtyArea, imgDrawingParams& aImgParams, float aOpacity = 1.0f, const gfxRect* aOverrideBBox = nullptr); /** * Returns the post-filter area that could be dirtied when the given * pre-filter area of aFilteredFrame changes. * @param aPreFilterDirtyRegion The pre-filter area of aFilteredFrame that * has changed, relative to aFilteredFrame, in app units. */ static nsRegion GetPostFilterDirtyArea(nsIFrame* aFilteredFrame, const nsRegion& aPreFilterDirtyRegion); /** * Returns the pre-filter area that is needed from aFilteredFrame when the * given post-filter area needs to be repainted. * @param aPostFilterDirtyRegion The post-filter area that is dirty, relative * to aFilteredFrame, in app units. */ static nsRegion GetPreFilterNeededArea( nsIFrame* aFilteredFrame, const nsTArray& aFilterFrames, const nsRegion& aPostFilterDirtyRegion); /** * Returns the post-filter ink 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 ink overflow rect of * aFilteredFrame, if non-null. */ static Maybe GetPostFilterBounds( nsIFrame* aFilteredFrame, const nsTArray& aFilterFrames, const gfxRect* aOverrideBBox = nullptr, const nsRect* aPreFilterBounds = nullptr); /** * Try to build WebRender filters for a frame if the filters applied to it are * supported. aInitialized is set to true if the filter has been initialized * and false otherwise (e.g. a bad url). If aInitialized is false the filter * the filter contents should not be drawn. */ static bool BuildWebRenderFilters( nsIFrame* aFilteredFrame, mozilla::Span aFilters, StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters, bool& aInitialized); private: /** * @param aTargetFrame The frame of the filtered element under consideration, * may be null. * @param aTargetContent The filtered element itself. * @param aMetrics The metrics to resolve SVG lengths against. * @param aFilterChain The list of filters to apply. * @param aFilterFrames The frames for the filters in the chain. * @param aFilterInputIsTainted Describes whether the SourceImage / * SourceAlpha input is tainted. This affects whether feDisplacementMap * will respect the filter input as its map input. * @param aPaintCallback [optional] The callback that Render() should use to * paint. Only required if you will call Render(). * @param aPaintTransform The transform to apply to convert to * aTargetFrame's SVG user space. Only used when painting. * @param aPostFilterDirtyRegion [optional] The post-filter area * that has to be repainted, in app units. Only required if you will * call ComputeSourceNeededRect() or Render(). * @param aPreFilterDirtyRegion [optional] The pre-filter area of * the filtered element that changed, in app units. Only required if you * will call ComputePostFilterDirtyRegion(). * @param aPreFilterInkOverflowRectOverride [optional] Use a different * ink overflow rect for the target element. * @param aOverrideBBox [optional] Use a different SVG bbox for the target * element. Must be non-null if aTargetFrame is null. */ FilterInstance( nsIFrame* aTargetFrame, nsIContent* aTargetContent, const UserSpaceMetrics& aMetrics, Span aFilterChain, const nsTArray& aFilterFrames, bool aFilterInputIsTainted, const SVGIntegrationUtils::SVGFilterPaintCallback& aPaintCallback, const gfxMatrix& aPaintTransform, const nsRegion* aPostFilterDirtyRegion = nullptr, const nsRegion* aPreFilterDirtyRegion = nullptr, const nsRect* aPreFilterInkOverflowRectOverride = nullptr, const gfxRect* aOverrideBBox = nullptr); static bool BuildWebRenderFiltersImpl( nsIFrame* aFilteredFrame, mozilla::Span aFilters, StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters, bool& aInitialized); /** * Returns true if the filter instance was created successfully. */ bool IsInitialized() const { return mInitialized; } /** * Draws the filter output into aDrawTarget. The area that * needs to be painted must have been specified before calling this method * by passing it as the aPostFilterDirtyRegion argument to the * FilterInstance constructor. */ void Render(gfxContext* aCtx, imgDrawingParams& aImgParams, float aOpacity = 1.0f); const FilterDescription& ExtractDescriptionAndAdditionalImages( nsTArray>& aOutAdditionalImages) { aOutAdditionalImages = std::move(mInputImages); return mFilterDescription; } /** * Sets the aPostFilterDirtyRegion outparam to the post-filter area in frame * space 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 * aPreFilterDirtyRegion argument to the FilterInstance constructor. */ nsRegion ComputePostFilterDirtyRegion(); /** * 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. */ nsRect ComputePostFilterExtents(); /** * 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 * aPostFilterDirtyRegion argument to the FilterInstance constructor. */ nsRect ComputeSourceNeededRect(); struct SourceInfo { // Specifies which parts of the source need to be rendered. // Set by ComputeNeededBoxes(). nsIntRect mNeededBounds; // The surface that contains the input rendering. // Set by BuildSourceImage / BuildSourcePaint. RefPtr mSourceSurface; // The position and size of mSourceSurface in filter space. // Set by BuildSourceImage / BuildSourcePaint. IntRect mSurfaceRect; }; /** * Creates a SourceSurface for either the FillPaint or StrokePaint graph * nodes */ void BuildSourcePaint(SourceInfo* aSource, imgDrawingParams& aImgParams); /** * Creates a SourceSurface for either the FillPaint and StrokePaint graph * nodes, fills its contents and assigns it to mFillPaint.mSourceSurface and * mStrokePaint.mSourceSurface respectively. */ void BuildSourcePaints(imgDrawingParams& aImgParams); /** * Creates the SourceSurface for the SourceGraphic graph node, paints its * contents, and assigns it to mSourceGraphic.mSourceSurface. */ void BuildSourceImage(DrawTarget* aDest, imgDrawingParams& aImgParams, mozilla::gfx::FilterNode* aFilter, mozilla::gfx::FilterNode* aSource, const mozilla::gfx::Rect& aSourceRect); /** * Build the list of FilterPrimitiveDescriptions that describes the filter's * filter primitives and their connections. This populates * mPrimitiveDescriptions and mInputImages. aFilterInputIsTainted describes * whether the SourceGraphic is tainted. */ nsresult BuildPrimitives(Span aFilterChain, const nsTArray& aFilterFrames, bool aFilterInputIsTainted); /** * Add to the list of FilterPrimitiveDescriptions for a particular SVG * reference filter or CSS filter. This populates mPrimitiveDescriptions and * mInputImages. aInputIsTainted describes whether the input to aFilter is * tainted. */ nsresult BuildPrimitivesForFilter( const StyleFilter& aFilter, SVGFilterFrame* aFilterFrame, bool aInputIsTainted, nsTArray& aPrimitiveDescriptions); /** * Computes the filter space bounds of the areas that we actually *need* from * the filter sources, based on the value of mPostFilterDirtyRegion. * This sets mNeededBounds on the corresponding SourceInfo structs. */ void ComputeNeededBoxes(); /** * Returns the output bounds of the final FilterPrimitiveDescription. */ nsIntRect OutputFilterSpaceBounds() const; /** * Compute the scale factors between user space and filter space. */ bool ComputeUserSpaceToFilterSpaceScale(); /** * Transform a rect between user space and filter space. */ gfxRect UserSpaceToFilterSpace(const gfxRect& aUserSpace) const; gfxRect FilterSpaceToUserSpace(const gfxRect& aFilterSpaceRect) const; /** * Converts an nsRect or an nsRegion that is relative to a filtered frame's * origin (i.e. the top-left corner of its border box) into filter space, * rounding out. * Returns the entire filter region if aRect / aRegion is null, or if the * result is too large to be stored in an nsIntRect. */ nsIntRect FrameSpaceToFilterSpace(const nsRect* aRect) const; nsIntRegion FrameSpaceToFilterSpace(const nsRegion* aRegion) const; /** * Converts an nsIntRect or an nsIntRegion from filter space into the space * that is relative to a filtered frame's origin (i.e. the top-left corner * of its border box) in app units, rounding out. */ nsRect FilterSpaceToFrameSpace(const nsIntRect& aRect) const; nsRegion FilterSpaceToFrameSpace(const nsIntRegion& aRegion) const; /** * 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. */ gfxMatrix GetUserSpaceToFrameSpaceInCSSPxTransform() const; bool ComputeTargetBBoxInFilterSpace(); /** * The frame for the element that is currently being filtered. */ nsIFrame* mTargetFrame; /** * The filtered element. */ nsIContent* mTargetContent; /** * The user space metrics of the filtered frame. */ const UserSpaceMetrics& mMetrics; const SVGFilterPaintCallback& mPaintCallback; /** * The SVG bbox of the element that is being filtered, in user space. */ gfxRect mTargetBBox; /** * The SVG bbox of the element that is being filtered, in filter space. */ nsIntRect mTargetBBoxInFilterSpace; /** * Transform rects between filter space and frame space in CSS pixels. */ gfxMatrix mFilterSpaceToFrameSpaceInCSSPxTransform; gfxMatrix mFrameSpaceInCSSPxToFilterSpaceTransform; /** * The scale factors between user space and filter space. */ gfx::MatrixScalesDouble mUserSpaceToFilterSpaceScale; gfx::MatrixScalesDouble mFilterSpaceToUserSpaceScale; /** * Pre-filter paint bounds of the element that is being filtered, in filter * space. */ nsIntRect mTargetBounds; /** * The dirty area that needs to be repainted, in filter space. */ nsIntRegion mPostFilterDirtyRegion; /** * The pre-filter area of the filtered element that changed, in filter space. */ nsIntRegion mPreFilterDirtyRegion; SourceInfo mSourceGraphic; SourceInfo mFillPaint; SourceInfo mStrokePaint; /** * The transform to the SVG user space of mTargetFrame. */ gfxMatrix mPaintTransform; nsTArray> mInputImages; FilterDescription mFilterDescription; bool mInitialized; }; } // namespace mozilla #endif // LAYOUT_SVG_FILTERINSTANCE_H_