SVGIntegrationUtils.h (11342B)
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_SVGINTEGRATIONUTILS_H_ 8 #define LAYOUT_SVG_SVGINTEGRATIONUTILS_H_ 9 10 #include "ImgDrawResult.h" 11 #include "gfxMatrix.h" 12 #include "gfxRect.h" 13 #include "mozilla/ServoStyleConsts.h" 14 #include "mozilla/gfx/Rect.h" 15 #include "mozilla/webrender/WebRenderTypes.h" 16 #include "nsRegionFwd.h" 17 18 class gfxContext; 19 class gfxDrawable; 20 class nsIFrame; 21 struct nsPoint; 22 struct nsRect; 23 struct nsSize; 24 25 // zipstruct.h defines this and causes issues if in the same translation unit. 26 #undef UNSUPPORTED 27 28 enum class WrFiltersStatus { 29 // Image will be rendered unftilered - the filter graph contains invalid refs 30 // (which SVG spec states will be rendered as if there is no filter graph). 31 UNSUPPORTED = 0, 32 // Image will be rendered unfiltered - the filter graph provided is 33 // excessively costly to render and has been dropped (per SVG spec we can do 34 // this to preserve user experience). 35 DISABLED_FOR_PERFORMANCE = 1, 36 // Image will be rendered using blob fallback (software rendering) due to 37 // unsupported operations (in either the CSS or SVGFE path). 38 BLOB_FALLBACK = 2, 39 // Image will be rendered using a simple CSS filter chain in WebRender. 40 CHAIN = 3, 41 // Filter graph will be rendered using WebRender SVGFE code path, this can 42 // handle any kind of filter graph consisting of supported operations (most 43 // operations are supported). 44 SVGFE = 4, 45 }; 46 47 struct WrFiltersHolder { 48 // TODO(Bug 1899691): Better to use AutoTArray here... 49 nsTArray<mozilla::wr::FilterOp> filters; 50 nsTArray<mozilla::wr::WrFilterData> filter_datas; 51 mozilla::Maybe<nsRect> post_filters_clip; 52 // This exists just to own the values long enough for them to be copied into 53 // rust. 54 nsTArray<nsTArray<float>> values; 55 }; 56 57 namespace mozilla { 58 class nsDisplayList; 59 class nsDisplayListBuilder; 60 61 /** 62 * Whether we're dealing with a backdrop-filter or a filter. 63 */ 64 enum class StyleFilterType : uint8_t { BackdropFilter, Filter }; 65 66 namespace gfx { 67 class DrawTarget; 68 } // namespace gfx 69 70 /** 71 * Integration of SVG effects (clipPath clipping, masking and filters) into 72 * regular display list based painting and hit-testing. 73 */ 74 class SVGIntegrationUtils final { 75 using DrawTarget = gfx::DrawTarget; 76 using IntRect = gfx::IntRect; 77 using imgDrawingParams = image::imgDrawingParams; 78 79 public: 80 /** 81 * Returns true if SVG effects that affect the overflow of the given frame 82 * are currently applied to the frame. 83 */ 84 static bool UsingOverflowAffectingEffects(const nsIFrame* aFrame); 85 86 /** 87 * Returns true if SVG effects are currently applied to this frame. 88 */ 89 static bool UsingEffectsForFrame(const nsIFrame* aFrame); 90 91 /** 92 * Returns the size of the union of the border-box rects of all of 93 * aNonSVGFrame's continuations. 94 */ 95 static nsSize GetContinuationUnionSize(nsIFrame* aNonSVGFrame); 96 97 /** 98 * When SVG effects need to resolve percentage, userSpaceOnUse lengths, they 99 * need a coordinate context to resolve them against. This method provides 100 * that coordinate context for non-SVG frames with SVG effects applied to 101 * them. The gfxSize returned is the size of the union of all of the given 102 * frame's continuations' border boxes, converted to SVG user units (equal to 103 * CSS px units), as required by the SVG code. 104 */ 105 static gfx::Size GetSVGCoordContextForNonSVGFrame(nsIFrame* aNonSVGFrame); 106 107 /** 108 * SVG effects such as SVG filters, masking and clipPath may require an SVG 109 * "bbox" for the element they're being applied to in order to make decisions 110 * about positioning, and to resolve various lengths against. This method 111 * provides the "bbox" for non-SVG frames. The bbox returned is in CSS px 112 * units, and aUnionContinuations decide whether bbox contains the area of 113 * current frame only or the union of all aNonSVGFrame's continuations' 114 * overflow areas, relative to the top-left of the union of all aNonSVGFrame's 115 * continuations' border box rects. 116 */ 117 static gfxRect GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame, 118 bool aUnionContinuations); 119 120 /** 121 * Used to adjust a frame's pre-effects ink overflow rect to take account 122 * of SVG effects. 123 * 124 * XXX This method will not do the right thing for frames with continuations. 125 * It really needs all the continuations to have been reflowed before being 126 * called, but we currently call it on each continuation as its overflow 127 * rects are set during the reflow of each particular continuation. Gecko's 128 * current reflow architecture does not allow us to set the overflow rects 129 * for a whole chain of continuations for a given element at the point when 130 * the last continuation is reflowed. See: 131 * http://groups.google.com/group/mozilla.dev.tech.layout/msg/6b179066f3051f65 132 */ 133 static nsRect ComputePostEffectsInkOverflowRect( 134 nsIFrame* aFrame, const nsRect& aPreEffectsOverflowRect); 135 136 /** 137 * Figure out which area of the source is needed given an area to 138 * repaint 139 */ 140 static nsRect GetRequiredSourceForInvalidArea(nsIFrame* aFrame, 141 const nsRect& aDirtyRect); 142 143 /** 144 * Returns true if the given point is not clipped out by effects. 145 * @param aPt in appunits relative to aFrame 146 */ 147 static bool HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt); 148 149 struct MOZ_STACK_CLASS PaintFramesParams { 150 gfxContext& ctx; 151 nsIFrame* frame; 152 nsRect dirtyRect; 153 nsRect borderArea; 154 nsDisplayListBuilder* builder; 155 bool handleOpacity; // If true, PaintMaskAndClipPath/ PaintFilter should 156 // apply css opacity. 157 Maybe<LayoutDeviceRect> maskRect; 158 imgDrawingParams& imgParams; 159 160 explicit PaintFramesParams(gfxContext& aCtx, nsIFrame* aFrame, 161 const nsRect& aDirtyRect, 162 const nsRect& aBorderArea, 163 nsDisplayListBuilder* aBuilder, 164 bool aHandleOpacity, 165 imgDrawingParams& aImgParams) 166 : ctx(aCtx), 167 frame(aFrame), 168 dirtyRect(aDirtyRect), 169 borderArea(aBorderArea), 170 builder(aBuilder), 171 handleOpacity(aHandleOpacity), 172 imgParams(aImgParams) {} 173 }; 174 175 // This should use FunctionRef instead of std::function because we don't need 176 // to take ownership of the function. See bug 1490781. 177 static void PaintMaskAndClipPath(const PaintFramesParams& aParams, 178 const std::function<void()>& aPaintChild); 179 180 /** 181 * Paint mask of frame onto a given context, aParams.ctx. 182 * aParams.ctx must contain an A8 surface. Returns false if the mask 183 * didn't get painted and should be ignored at the call site. 184 * isMaskComplete is an outparameter returning whether the mask is complete. 185 * Incomplete masks should not be drawn and the proper fallback behaviour 186 * depends on if the masked element is html or svg. 187 */ 188 static bool PaintMask(const PaintFramesParams& aParams, 189 bool& aOutIsMaskComplete); 190 191 /** 192 * Paint the frame contents. 193 * SVG frames will have had matrix propagation set to false already. 194 * Non-SVG frames have to do their own thing. 195 * The caller will do a Save()/Restore() as necessary so feel free 196 * to mess with context state. 197 * The context will be configured to use the "user space" coordinate 198 * system if passing aTransform/aDirtyRect, or untouched otherwise. 199 * @param aImgParams the params to draw with. 200 * @param aTransform the user-to-device space matrix, if painting with 201 * filters. 202 * @param aDirtyRect the dirty rect *in user space pixels* 203 */ 204 using SVGFilterPaintCallback = std::function<void( 205 gfxContext& aContext, imgDrawingParams&, const gfxMatrix* aTransform, 206 const nsIntRect* aDirtyRect)>; 207 208 /** 209 * Paint non-SVG frame with filter and opacity effect. 210 */ 211 static void PaintFilter(const PaintFramesParams& aParams, 212 Span<const StyleFilter> aFilters, 213 const SVGFilterPaintCallback& aCallback); 214 215 /** 216 * Build WebRender filters for a frame with CSS filters applied to it. 217 */ 218 static WrFiltersStatus CreateWebRenderCSSFilters( 219 Span<const StyleFilter> aFilters, nsIFrame* aFrame, 220 WrFiltersHolder& aWrFilters); 221 222 /** 223 * Try to build WebRender filters for a frame with SVG filters applied to it 224 * if the filters are supported. 225 */ 226 static WrFiltersStatus BuildWebRenderFilters( 227 nsIFrame* aFilteredFrame, Span<const StyleFilter> aFilters, 228 StyleFilterType aStyleFilterType, WrFiltersHolder& aWrFilters, 229 const nsPoint& aOffsetForSVGFilters); 230 231 /** 232 * Check if the filters present on |aFrame| are supported by WebRender. 233 */ 234 static bool CanCreateWebRenderFiltersForFrame(nsIFrame* aFrame); 235 236 /** 237 * Check if |aFrame| uses any SVG effects that cannot be rendered in the 238 * compositor. 239 */ 240 static bool UsesSVGEffectsNotSupportedInCompositor(nsIFrame* aFrame); 241 242 /** 243 * @param aRenderingContext the target rendering context in which the paint 244 * server will be rendered 245 * @param aTarget the target frame onto which the paint server will be 246 * rendered 247 * @param aPaintServer a first-continuation frame to use as the source 248 * @param aFilter a filter to be applied when scaling 249 * @param aDest the area the paint server image should be mapped to 250 * @param aFill the area to be filled with copies of the paint server image 251 * @param aAnchor a point in aFill which we will ensure is pixel-aligned in 252 * the output 253 * @param aDirty pixels outside this area may be skipped 254 * @param aPaintServerSize the size that would be filled when using 255 * background-repeat:no-repeat and background-size:auto. For normal background 256 * images, this would be the intrinsic size of the image; for gradients and 257 * patterns this would be the whole target frame fill area. 258 * @param aFlags pass SyncDecodeImages and any images in the paint 259 * server will be decoding synchronously if they are not decoded already. 260 */ 261 enum class DecodeFlag { 262 SyncDecodeImages, 263 }; 264 using DecodeFlags = EnumSet<DecodeFlag>; 265 266 static already_AddRefed<gfxDrawable> DrawableFromPaintServer( 267 nsIFrame* aFrame, nsIFrame* aTarget, const nsSize& aPaintServerSize, 268 const gfx::IntSize& aRenderSize, const DrawTarget* aDrawTarget, 269 const gfxMatrix& aContextMatrix, DecodeFlags aFlags); 270 271 /** 272 * For non-SVG frames, this gives the offset to the frame's "user space". 273 * For SVG frames, this returns a zero offset. 274 */ 275 static nsPoint GetOffsetToBoundingBox(nsIFrame* aFrame); 276 277 /** 278 * The offset between the reference frame and the bounding box of the 279 * target frame in device units. 280 */ 281 static gfxPoint GetOffsetToUserSpaceInDevPx(nsIFrame* aFrame, 282 const PaintFramesParams& aParams); 283 }; 284 285 } // namespace mozilla 286 287 #endif // LAYOUT_SVG_SVGINTEGRATIONUTILS_H_