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