FilterInstance.h (16674B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef LAYOUT_SVG_FILTERINSTANCE_H_ 8 #define LAYOUT_SVG_FILTERINSTANCE_H_ 9 10 #include "FilterDescription.h" 11 #include "gfxMatrix.h" 12 #include "gfxPoint.h" 13 #include "gfxRect.h" 14 #include "mozilla/SVGIntegrationUtils.h" 15 #include "mozilla/ServoStyleConsts.h" 16 #include "mozilla/gfx/2D.h" 17 #include "mozilla/webrender/WebRenderTypes.h" 18 #include "nsCOMPtr.h" 19 #include "nsHashKeys.h" 20 #include "nsPoint.h" 21 #include "nsRect.h" 22 #include "nsSize.h" 23 #include "nsTArray.h" 24 25 class gfxContext; 26 class nsIContent; 27 class nsIFrame; 28 struct WrFiltersHolder; 29 30 namespace mozilla { 31 class ISVGFilterObserverList; 32 class SVGFilterFrame; 33 34 namespace dom { 35 class UserSpaceMetrics; 36 } // namespace dom 37 38 namespace image { 39 struct imgDrawingParams; 40 } 41 42 /** 43 * This class performs all filter processing. 44 * 45 * We build a graph of the filter image data flow, essentially 46 * converting the filter graph to SSA. This lets us easily propagate 47 * analysis data (such as bounding-boxes) over the filter primitive graph. 48 * 49 * Definition of "filter space": filter space is a coordinate system that is 50 * aligned with the user space of the filtered element, with its origin located 51 * at the top left of the filter region, and with one unit equal in size to one 52 * pixel of the offscreen surface into which the filter output would/will be 53 * painted. 54 * 55 * The definition of "filter region" can be found here: 56 * http://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion 57 */ 58 class FilterInstance { 59 using IntRect = gfx::IntRect; 60 using SourceSurface = gfx::SourceSurface; 61 using DrawTarget = gfx::DrawTarget; 62 using FilterPrimitiveDescription = gfx::FilterPrimitiveDescription; 63 using FilterDescription = gfx::FilterDescription; 64 using UserSpaceMetrics = dom::UserSpaceMetrics; 65 using imgDrawingParams = image::imgDrawingParams; 66 using SVGFilterPaintCallback = SVGIntegrationUtils::SVGFilterPaintCallback; 67 68 public: 69 /** 70 * Create a FilterDescription for the supplied filter. All coordinates in 71 * the description are in filter space. 72 * @param aFilterInputIsTainted Describes whether the SourceImage / 73 * SourceAlpha input is tainted. This affects whether feDisplacementMap 74 * will respect the filter input as its map input, and it affects the 75 * IsTainted() state on the filter primitives in the FilterDescription. 76 * "Tainted" is a term from the filters spec and means security-sensitive 77 * content, i.e. pixels that JS should not be able to read in any way. 78 * @param aOutAdditionalImages Will contain additional images needed to 79 * render the filter (from feImage primitives). 80 * @return A FilterDescription describing the filter. 81 */ 82 static FilterDescription GetFilterDescription( 83 nsIContent* aFilteredElement, Span<const StyleFilter> aFilterChain, 84 ISVGFilterObserverList* aFiltersObserverList, bool aFilterInputIsTainted, 85 const UserSpaceMetrics& aMetrics, const gfxRect& aBBox, 86 nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages); 87 88 /** 89 * Paint the given filtered frame. 90 * @param aDirtyArea The area than needs to be painted, in aFilteredFrame's 91 * frame space (i.e. relative to its origin, the top-left corner of its 92 * border box). 93 */ 94 static void PaintFilteredFrame( 95 nsIFrame* aFilteredFrame, Span<const StyleFilter> aFilterChain, 96 const nsTArray<SVGFilterFrame*>& aFilterFrames, gfxContext* aCtx, 97 const SVGFilterPaintCallback& aPaintCallback, const nsRegion* aDirtyArea, 98 imgDrawingParams& aImgParams, float aOpacity = 1.0f, 99 const gfxRect* aOverrideBBox = nullptr); 100 101 /** 102 * Returns the post-filter area that could be dirtied when the given 103 * pre-filter area of aFilteredFrame changes. 104 * @param aPreFilterDirtyRegion The pre-filter area of aFilteredFrame that 105 * has changed, relative to aFilteredFrame, in app units. 106 */ 107 static nsRegion GetPostFilterDirtyArea(nsIFrame* aFilteredFrame, 108 const nsRegion& aPreFilterDirtyRegion); 109 110 /** 111 * Returns the pre-filter area that is needed from aFilteredFrame when the 112 * given post-filter area needs to be repainted. 113 * @param aPostFilterDirtyRegion The post-filter area that is dirty, relative 114 * to aFilteredFrame, in app units. 115 */ 116 static nsRegion GetPreFilterNeededArea( 117 nsIFrame* aFilteredFrame, const nsTArray<SVGFilterFrame*>& aFilterFrames, 118 const nsRegion& aPostFilterDirtyRegion); 119 120 /** 121 * Returns the post-filter ink overflow rect (paint bounds) of 122 * aFilteredFrame. 123 * @param aOverrideBBox A user space rect, in user units, that should be used 124 * as aFilteredFrame's bbox ('bbox' is a specific SVG term), if non-null. 125 * @param aPreFilterBounds The pre-filter ink overflow rect of 126 * aFilteredFrame, if non-null. 127 */ 128 static Maybe<nsRect> GetPostFilterBounds( 129 nsIFrame* aFilteredFrame, const nsTArray<SVGFilterFrame*>& aFilterFrames, 130 const gfxRect* aOverrideBBox = nullptr, 131 const nsRect* aPreFilterBounds = nullptr); 132 133 /** 134 * Try to build WebRender filters for a frame if the filters applied to it are 135 * supported, returns a status that indicates which code path will handle the 136 * filters on this frame, or if we must fall back to blob image. 137 */ 138 static WrFiltersStatus BuildWebRenderFilters( 139 nsIFrame* aFilteredFrame, 140 mozilla::Span<const mozilla::StyleFilter> aFilters, 141 StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters, 142 const nsPoint& aOffsetForSVGFilters); 143 144 /** 145 * Try to build WebRender SVG filter graph for a frame based on SVG and CSS 146 * filters. If given an unreasonably costly set of filters this can reject 147 * the entire filter graph (a behavior permitted by SVG spec). 148 * 149 * See WrFiltersStatus for possible outcomes. 150 * Prefs such as gfx.webrender.svg-filter-effects affect this. 151 */ 152 static WrFiltersStatus BuildWebRenderSVGFiltersImpl( 153 nsIFrame* aFilteredFrame, 154 mozilla::Span<const mozilla::StyleFilter> aFilters, 155 StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters, 156 const nsPoint& aOffsetForSVGFilters); 157 158 private: 159 /** 160 * @param aTargetFrame The frame of the filtered element under consideration, 161 * may be null. 162 * @param aTargetContent The filtered element itself. 163 * @param aMetrics The metrics to resolve SVG lengths against. 164 * @param aFilterChain The list of filters to apply. 165 * @param aFilterFrames The frames for the filters in the chain. 166 * @param aFilterInputIsTainted Describes whether the SourceImage / 167 * SourceAlpha input is tainted. This affects whether feDisplacementMap 168 * will respect the filter input as its map input. 169 * @param aPaintCallback [optional] The callback that Render() should use to 170 * paint. Only required if you will call Render(). 171 * @param aPaintTransform The transform to apply to convert to 172 * aTargetFrame's SVG user space. Only used when painting. 173 * @param aPostFilterDirtyRegion [optional] The post-filter area 174 * that has to be repainted, in app units. Only required if you will 175 * call ComputeSourceNeededRect() or Render(). 176 * @param aPreFilterDirtyRegion [optional] The pre-filter area of 177 * the filtered element that changed, in app units. Only required if you 178 * will call ComputePostFilterDirtyRegion(). 179 * @param aPreFilterInkOverflowRectOverride [optional] Use a different 180 * ink overflow rect for the target element. 181 * @param aOverrideBBox [optional] Use a different SVG bbox for the target 182 * element. Must be non-null if aTargetFrame is null. 183 * @param aFilterSpaceBoundsNotSnapped [optional] The calculated bbox in 184 * userspace can be returend in the provided outparam. 185 */ 186 FilterInstance( 187 nsIFrame* aTargetFrame, nsIContent* aTargetContent, 188 const UserSpaceMetrics& aMetrics, Span<const StyleFilter> aFilterChain, 189 const nsTArray<SVGFilterFrame*>& aFilterFrames, 190 bool aFilterInputIsTainted, 191 const SVGIntegrationUtils::SVGFilterPaintCallback& aPaintCallback, 192 const gfxMatrix& aPaintTransform, 193 const nsRegion* aPostFilterDirtyRegion = nullptr, 194 const nsRegion* aPreFilterDirtyRegion = nullptr, 195 const nsRect* aPreFilterInkOverflowRectOverride = nullptr, 196 const gfxRect* aOverrideBBox = nullptr, 197 gfxRect* aFilterSpaceBoundsNotSnapped = nullptr); 198 199 static WrFiltersStatus BuildWebRenderFiltersImpl( 200 nsIFrame* aFilteredFrame, 201 mozilla::Span<const mozilla::StyleFilter> aFilters, 202 StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters); 203 204 /** 205 * Returns true if the filter instance was created successfully. 206 */ 207 bool IsInitialized() const { return mInitialized; } 208 209 /** 210 * Draws the filter output into aDrawTarget. The area that 211 * needs to be painted must have been specified before calling this method 212 * by passing it as the aPostFilterDirtyRegion argument to the 213 * FilterInstance constructor. 214 */ 215 void Render(gfxContext* aCtx, imgDrawingParams& aImgParams, 216 float aOpacity = 1.0f); 217 218 const FilterDescription& ExtractDescriptionAndAdditionalImages( 219 nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages) { 220 aOutAdditionalImages = std::move(mInputImages); 221 return mFilterDescription; 222 } 223 224 /** 225 * Sets the aPostFilterDirtyRegion outparam to the post-filter area in frame 226 * space that would be dirtied by mTargetFrame when a given 227 * pre-filter area of mTargetFrame is dirtied. The pre-filter area must have 228 * been specified before calling this method by passing it as the 229 * aPreFilterDirtyRegion argument to the FilterInstance constructor. 230 */ 231 nsRegion ComputePostFilterDirtyRegion(); 232 233 /** 234 * Sets the aPostFilterExtents outparam to the post-filter bounds in frame 235 * space for the whole filter output. This is not necessarily equivalent to 236 * the area that would be dirtied in the result when the entire pre-filter 237 * area is dirtied, because some filter primitives can generate output 238 * without any input. 239 */ 240 nsRect ComputePostFilterExtents(); 241 242 /** 243 * Sets the aDirty outparam to the pre-filter bounds in frame space of the 244 * area of mTargetFrame that is needed in order to paint the filtered output 245 * for a given post-filter dirtied area. The post-filter area must have been 246 * specified before calling this method by passing it as the 247 * aPostFilterDirtyRegion argument to the FilterInstance constructor. 248 */ 249 nsRect ComputeSourceNeededRect(); 250 251 struct SourceInfo { 252 // Specifies which parts of the source need to be rendered. 253 // Set by ComputeNeededBoxes(). 254 nsIntRect mNeededBounds; 255 256 // The surface that contains the input rendering. 257 // Set by BuildSourceImage / BuildSourcePaint. 258 RefPtr<SourceSurface> mSourceSurface; 259 260 // The position and size of mSourceSurface in filter space. 261 // Set by BuildSourceImage / BuildSourcePaint. 262 IntRect mSurfaceRect; 263 }; 264 265 /** 266 * Creates a SourceSurface for either the FillPaint or StrokePaint graph 267 * nodes 268 */ 269 void BuildSourcePaint(SourceInfo* aSource, imgDrawingParams& aImgParams); 270 271 /** 272 * Creates a SourceSurface for either the FillPaint and StrokePaint graph 273 * nodes, fills its contents and assigns it to mFillPaint.mSourceSurface and 274 * mStrokePaint.mSourceSurface respectively. 275 */ 276 void BuildSourcePaints(imgDrawingParams& aImgParams); 277 278 /** 279 * Creates the SourceSurface for the SourceGraphic graph node, paints its 280 * contents, and assigns it to mSourceGraphic.mSourceSurface. 281 */ 282 void BuildSourceImage(DrawTarget* aDest, imgDrawingParams& aImgParams, 283 mozilla::gfx::FilterNode* aFilter, 284 mozilla::gfx::FilterNode* aSource, 285 const mozilla::gfx::Rect& aSourceRect); 286 287 /** 288 * Build the list of FilterPrimitiveDescriptions that describes the filter's 289 * filter primitives and their connections. This populates 290 * mPrimitiveDescriptions and mInputImages. aFilterInputIsTainted describes 291 * whether the SourceGraphic is tainted. 292 */ 293 nsresult BuildPrimitives(Span<const StyleFilter> aFilterChain, 294 const nsTArray<SVGFilterFrame*>& aFilterFrames, 295 bool aFilterInputIsTainted); 296 297 /** 298 * Add to the list of FilterPrimitiveDescriptions for a particular SVG 299 * reference filter or CSS filter. This populates mPrimitiveDescriptions and 300 * mInputImages. aInputIsTainted describes whether the input to aFilter is 301 * tainted. 302 */ 303 nsresult BuildPrimitivesForFilter( 304 const StyleFilter& aFilter, SVGFilterFrame* aFilterFrame, 305 bool aInputIsTainted, 306 nsTArray<FilterPrimitiveDescription>& aPrimitiveDescriptions); 307 308 /** 309 * Computes the filter space bounds of the areas that we actually *need* from 310 * the filter sources, based on the value of mPostFilterDirtyRegion. 311 * This sets mNeededBounds on the corresponding SourceInfo structs. 312 */ 313 void ComputeNeededBoxes(); 314 315 /** 316 * Returns the output bounds of the final FilterPrimitiveDescription. 317 */ 318 nsIntRect OutputFilterSpaceBounds() const; 319 320 /** 321 * Compute the scale factors between user space and filter space. 322 */ 323 bool ComputeUserSpaceToFilterSpaceScale(); 324 325 /** 326 * Transform a rect between user space and filter space. 327 */ 328 gfxRect UserSpaceToFilterSpace(const gfxRect& aUserSpace) const; 329 gfxRect FilterSpaceToUserSpace(const gfxRect& aFilterSpaceRect) const; 330 331 /** 332 * Converts an nsRect or an nsRegion that is relative to a filtered frame's 333 * origin (i.e. the top-left corner of its border box) into filter space, 334 * rounding out. 335 * Returns the entire filter region if aRect / aRegion is null, or if the 336 * result is too large to be stored in an nsIntRect. 337 */ 338 nsIntRect FrameSpaceToFilterSpace(const nsRect* aRect) const; 339 nsIntRegion FrameSpaceToFilterSpace(const nsRegion* aRegion) const; 340 341 /** 342 * Converts an nsIntRect or an nsIntRegion from filter space into the space 343 * that is relative to a filtered frame's origin (i.e. the top-left corner 344 * of its border box) in app units, rounding out. 345 */ 346 nsRect FilterSpaceToFrameSpace(const nsIntRect& aRect) const; 347 nsRegion FilterSpaceToFrameSpace(const nsIntRegion& aRegion) const; 348 349 /** 350 * Returns the transform from frame space to the coordinate space that 351 * GetCanvasTM transforms to. "Frame space" is the origin of a frame, aka the 352 * top-left corner of its border box, aka the top left corner of its mRect. 353 */ 354 gfxMatrix GetUserSpaceToFrameSpaceInCSSPxTransform() const; 355 356 bool ComputeTargetBBoxInFilterSpace(); 357 358 /** 359 * The frame for the element that is currently being filtered. 360 */ 361 nsIFrame* mTargetFrame; 362 363 /** 364 * The filtered element. 365 */ 366 nsIContent* mTargetContent; 367 368 /** 369 * The user space metrics of the filtered frame. 370 */ 371 const UserSpaceMetrics& mMetrics; 372 373 const SVGFilterPaintCallback& mPaintCallback; 374 375 /** 376 * The SVG bbox of the element that is being filtered, in user space. 377 */ 378 gfxRect mTargetBBox; 379 380 /** 381 * The SVG bbox of the element that is being filtered, in filter space. 382 */ 383 nsIntRect mTargetBBoxInFilterSpace; 384 385 /** 386 * The SVG filter element rect, in filter space, may be non-integer. 387 */ 388 gfxRect mFilterSpaceBoundsNotSnapped; 389 390 /** 391 * Transform rects between filter space and frame space in CSS pixels. 392 */ 393 gfxMatrix mFilterSpaceToFrameSpaceInCSSPxTransform; 394 gfxMatrix mFrameSpaceInCSSPxToFilterSpaceTransform; 395 396 /** 397 * The scale factors between user space and filter space. 398 */ 399 gfx::MatrixScalesDouble mUserSpaceToFilterSpaceScale; 400 gfx::MatrixScalesDouble mFilterSpaceToUserSpaceScale; 401 402 /** 403 * Pre-filter paint bounds of the element that is being filtered, in filter 404 * space. 405 */ 406 nsIntRect mTargetBounds; 407 408 /** 409 * The dirty area that needs to be repainted, in filter space. 410 */ 411 nsIntRegion mPostFilterDirtyRegion; 412 413 /** 414 * The pre-filter area of the filtered element that changed, in filter space. 415 */ 416 nsIntRegion mPreFilterDirtyRegion; 417 418 SourceInfo mSourceGraphic; 419 SourceInfo mFillPaint; 420 SourceInfo mStrokePaint; 421 422 /** 423 * The transform to the SVG user space of mTargetFrame. 424 */ 425 gfxMatrix mPaintTransform; 426 427 nsTArray<RefPtr<SourceSurface>> mInputImages; 428 FilterDescription mFilterDescription; 429 bool mInitialized; 430 }; 431 432 } // namespace mozilla 433 434 #endif // LAYOUT_SVG_FILTERINSTANCE_H_