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