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/. */
8 #include "SVGIntegrationUtils.h"
10 // Keep others in (case-insensitive) order:
11 #include "gfxDrawable.h"
14 #include "nsCSSAnonBoxes.h"
15 #include "nsCSSRendering.h"
16 #include "nsDisplayList.h"
17 #include "nsLayoutUtils.h"
18 #include "gfxContext.h"
19 #include "SVGPaintServerFrame.h"
20 #include "mozilla/gfx/Point.h"
21 #include "mozilla/gfx/gfxVars.h"
22 #include "mozilla/CSSClipPathInstance.h"
23 #include "mozilla/FilterInstance.h"
24 #include "mozilla/StaticPrefs_layers.h"
25 #include "mozilla/SVGClipPathFrame.h"
26 #include "mozilla/SVGObserverUtils.h"
27 #include "mozilla/SVGMaskFrame.h"
28 #include "mozilla/SVGUtils.h"
29 #include "mozilla/Unused.h"
30 #include "mozilla/dom/SVGElement.h"
32 using namespace mozilla::dom
;
33 using namespace mozilla::layers
;
34 using namespace mozilla::gfx
;
35 using namespace mozilla::image
;
40 * This class is used to get the pre-effects ink overflow rect of a frame,
41 * or, in the case of a frame with continuations, to collect the union of the
42 * pre-effects ink overflow rects of all the continuations. The result is
43 * relative to the origin (top left corner of the border box) of the frame, or,
44 * if the frame has continuations, the origin of the _first_ continuation.
46 class PreEffectsInkOverflowCollector
: public nsLayoutUtils::BoxCallback
{
49 * If the pre-effects ink overflow rect of the frame being examined
50 * happens to be known, it can be passed in as aCurrentFrame and its
51 * pre-effects ink overflow rect can be passed in as
52 * aCurrentFrameOverflowArea. This is just an optimization to save a
53 * frame property lookup - these arguments are optional.
55 PreEffectsInkOverflowCollector(nsIFrame
* aFirstContinuation
,
56 nsIFrame
* aCurrentFrame
,
57 const nsRect
& aCurrentFrameOverflowArea
,
59 : mFirstContinuation(aFirstContinuation
),
60 mCurrentFrame(aCurrentFrame
),
61 mCurrentFrameOverflowArea(aCurrentFrameOverflowArea
),
62 mInReflow(aInReflow
) {
63 NS_ASSERTION(!mFirstContinuation
->GetPrevContinuation(),
64 "We want the first continuation here");
67 virtual void AddBox(nsIFrame
* aFrame
) override
{
68 nsRect overflow
= (aFrame
== mCurrentFrame
)
69 ? mCurrentFrameOverflowArea
70 : PreEffectsInkOverflowRect(aFrame
, mInReflow
);
71 mResult
.UnionRect(mResult
,
72 overflow
+ aFrame
->GetOffsetTo(mFirstContinuation
));
75 nsRect
GetResult() const { return mResult
; }
78 static nsRect
PreEffectsInkOverflowRect(nsIFrame
* aFrame
, bool aInReflow
) {
79 nsRect
* r
= aFrame
->GetProperty(nsIFrame::PreEffectsBBoxProperty());
85 // Having PreTransformOverflowAreasProperty cached means
86 // InkOverflowRect() will return post-effect rect, which is not what
87 // we want. This function intentional reports pre-effect rect. But it does
88 // not matter if there is no SVG effect on this frame, since no effect
89 // means post-effect rect matches pre-effect rect.
91 // This function may be called during reflow or painting. We should only
92 // do this check in painting process since the PreEffectsBBoxProperty of
93 // continuations are not set correctly while reflowing.
94 if (SVGIntegrationUtils::UsingOverflowAffectingEffects(aFrame
) &&
96 OverflowAreas
* preTransformOverflows
=
97 aFrame
->GetProperty(nsIFrame::PreTransformOverflowAreasProperty());
99 MOZ_ASSERT(!preTransformOverflows
,
100 "InkOverflowRect() won't return the pre-effects rect!");
103 return aFrame
->InkOverflowRectRelativeToSelf();
106 nsIFrame
* mFirstContinuation
;
107 nsIFrame
* mCurrentFrame
;
108 const nsRect
& mCurrentFrameOverflowArea
;
114 * Gets the union of the pre-effects ink overflow rects of all of a frame's
115 * continuations, in "user space".
117 static nsRect
GetPreEffectsInkOverflowUnion(
118 nsIFrame
* aFirstContinuation
, nsIFrame
* aCurrentFrame
,
119 const nsRect
& aCurrentFramePreEffectsOverflow
,
120 const nsPoint
& aFirstContinuationToUserSpace
, bool aInReflow
) {
121 NS_ASSERTION(!aFirstContinuation
->GetPrevContinuation(),
122 "Need first continuation here");
123 PreEffectsInkOverflowCollector
collector(aFirstContinuation
, aCurrentFrame
,
124 aCurrentFramePreEffectsOverflow
,
126 // Compute union of all overflow areas relative to aFirstContinuation:
127 nsLayoutUtils::GetAllInFlowBoxes(aFirstContinuation
, &collector
);
128 // Return the result in user space:
129 return collector
.GetResult() + aFirstContinuationToUserSpace
;
133 * Gets the pre-effects ink overflow rect of aCurrentFrame in "user space".
135 static nsRect
GetPreEffectsInkOverflow(
136 nsIFrame
* aFirstContinuation
, nsIFrame
* aCurrentFrame
,
137 const nsPoint
& aFirstContinuationToUserSpace
) {
138 NS_ASSERTION(!aFirstContinuation
->GetPrevContinuation(),
139 "Need first continuation here");
140 PreEffectsInkOverflowCollector
collector(aFirstContinuation
, nullptr,
142 // Compute overflow areas of current frame relative to aFirstContinuation:
143 nsLayoutUtils::AddBoxesForFrame(aCurrentFrame
, &collector
);
144 // Return the result in user space:
145 return collector
.GetResult() + aFirstContinuationToUserSpace
;
148 bool SVGIntegrationUtils::UsingOverflowAffectingEffects(
149 const nsIFrame
* aFrame
) {
150 // Currently overflow don't take account of SVG or other non-absolute
151 // positioned clipping, or masking.
152 return aFrame
->StyleEffects()->HasFilters();
155 bool SVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame
* aFrame
) {
156 // Even when SVG display lists are disabled, returning true for SVG frames
157 // does not adversely affect any of our callers. Therefore we don't bother
158 // checking the SDL prefs here, since we don't know if we're being called for
159 // painting or hit-testing anyway.
160 const nsStyleSVGReset
* style
= aFrame
->StyleSVGReset();
161 const nsStyleEffects
* effects
= aFrame
->StyleEffects();
162 // TODO(cbrewster): remove backdrop-filter from this list once it is supported
163 // in preserve-3d cases.
164 return effects
->HasFilters() || effects
->HasBackdropFilters() ||
165 style
->HasClipPath() || style
->HasMask();
168 bool SVGIntegrationUtils::UsingMaskOrClipPathForFrame(const nsIFrame
* aFrame
) {
169 const nsStyleSVGReset
* style
= aFrame
->StyleSVGReset();
170 return style
->HasClipPath() || style
->HasMask();
173 bool SVGIntegrationUtils::UsingSimpleClipPathForFrame(const nsIFrame
* aFrame
) {
174 const nsStyleSVGReset
* style
= aFrame
->StyleSVGReset();
175 if (!style
->HasClipPath() || style
->HasMask()) {
179 const auto& clipPath
= style
->mClipPath
;
180 if (!clipPath
.IsShape()) {
184 return !clipPath
.AsShape()._0
->IsPolygon();
187 nsPoint
SVGIntegrationUtils::GetOffsetToBoundingBox(nsIFrame
* aFrame
) {
188 if (aFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
)) {
189 // Do NOT call GetAllInFlowRectsUnion for SVG - it will get the
190 // covered region relative to the SVGOuterSVGFrame, which is absolutely
191 // not what we want. SVG frames are always in user space, so they have
192 // no offset adjustment to make.
196 // The GetAllInFlowRectsUnion() call gets the union of the frame border-box
197 // rects over all continuations, relative to the origin (top-left of the
198 // border box) of its second argument (here, aFrame, the first continuation).
199 return -nsLayoutUtils::GetAllInFlowRectsUnion(aFrame
, aFrame
).TopLeft();
202 struct EffectOffsets
{
203 // The offset between the reference frame and the bounding box of the
204 // target frame in app unit.
205 nsPoint offsetToBoundingBox
;
206 // The offset between the reference frame and the bounding box of the
207 // target frame in app unit.
208 nsPoint offsetToUserSpace
;
209 // The offset between the reference frame and the bounding box of the
210 // target frame in device unit.
211 gfxPoint offsetToUserSpaceInDevPx
;
214 static EffectOffsets
ComputeEffectOffset(
215 nsIFrame
* aFrame
, const SVGIntegrationUtils::PaintFramesParams
& aParams
) {
216 EffectOffsets result
;
218 result
.offsetToBoundingBox
=
219 aParams
.builder
->ToReferenceFrame(aFrame
) -
220 SVGIntegrationUtils::GetOffsetToBoundingBox(aFrame
);
221 if (!aFrame
->IsFrameOfType(nsIFrame::eSVG
)) {
222 /* Snap the offset if the reference frame is not a SVG frame,
223 * since other frames will be snapped to pixel when rendering. */
224 result
.offsetToBoundingBox
=
225 nsPoint(aFrame
->PresContext()->RoundAppUnitsToNearestDevPixels(
226 result
.offsetToBoundingBox
.x
),
227 aFrame
->PresContext()->RoundAppUnitsToNearestDevPixels(
228 result
.offsetToBoundingBox
.y
));
231 // After applying only "aOffsetToBoundingBox", aParams.ctx would have its
232 // origin at the top left corner of frame's bounding box (over all
234 // However, SVG painting needs the origin to be located at the origin of the
235 // SVG frame's "user space", i.e. the space in which, for example, the
236 // frame's BBox lives.
237 // SVG geometry frames and foreignObject frames apply their own offsets, so
238 // their position is relative to their user space. So for these frame types,
239 // if we want aParams.ctx to be in user space, we first need to subtract the
240 // frame's position so that SVG painting can later add it again and the
241 // frame is painted in the right place.
242 gfxPoint toUserSpaceGfx
=
243 SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame
);
244 nsPoint toUserSpace
=
245 nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx
.x
)),
246 nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx
.y
)));
248 result
.offsetToUserSpace
= result
.offsetToBoundingBox
- toUserSpace
;
251 bool hasSVGLayout
= aFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
);
253 hasSVGLayout
|| result
.offsetToBoundingBox
== result
.offsetToUserSpace
,
254 "For non-SVG frames there shouldn't be any additional offset");
257 result
.offsetToUserSpaceInDevPx
= nsLayoutUtils::PointToGfxPoint(
258 result
.offsetToUserSpace
, aFrame
->PresContext()->AppUnitsPerDevPixel());
264 * Setup transform matrix of a gfx context by a specific frame. Move the
265 * origin of aParams.ctx to the user space of aFrame.
267 static EffectOffsets
MoveContextOriginToUserSpace(
268 nsIFrame
* aFrame
, const SVGIntegrationUtils::PaintFramesParams
& aParams
) {
269 EffectOffsets offset
= ComputeEffectOffset(aFrame
, aParams
);
271 aParams
.ctx
.SetMatrixDouble(aParams
.ctx
.CurrentMatrixDouble().PreTranslate(
272 offset
.offsetToUserSpaceInDevPx
));
277 gfxPoint
SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(
278 nsIFrame
* aFrame
, const PaintFramesParams
& aParams
) {
279 nsIFrame
* firstFrame
=
280 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame
);
281 EffectOffsets offset
= ComputeEffectOffset(firstFrame
, aParams
);
282 return offset
.offsetToUserSpaceInDevPx
;
286 nsSize
SVGIntegrationUtils::GetContinuationUnionSize(nsIFrame
* aNonSVGFrame
) {
287 NS_ASSERTION(!aNonSVGFrame
->IsFrameOfType(nsIFrame::eSVG
),
288 "SVG frames should not get here");
289 nsIFrame
* firstFrame
=
290 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame
);
291 return nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame
, firstFrame
).Size();
294 /* static */ gfx::Size
SVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(
295 nsIFrame
* aNonSVGFrame
) {
296 NS_ASSERTION(!aNonSVGFrame
->IsFrameOfType(nsIFrame::eSVG
),
297 "SVG frames should not get here");
298 nsIFrame
* firstFrame
=
299 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame
);
300 nsRect r
= nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame
, firstFrame
);
301 return gfx::Size(nsPresContext::AppUnitsToFloatCSSPixels(r
.width
),
302 nsPresContext::AppUnitsToFloatCSSPixels(r
.height
));
305 gfxRect
SVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(
306 nsIFrame
* aNonSVGFrame
, bool aUnionContinuations
) {
307 // Except for SVGOuterSVGFrame, we shouldn't be getting here with SVG
308 // frames at all. This function is for elements that are laid out using the
309 // CSS box model rules.
310 NS_ASSERTION(!aNonSVGFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
),
311 "Frames with SVG layout should not get here");
312 MOZ_ASSERT(!aNonSVGFrame
->IsFrameOfType(nsIFrame::eSVG
) ||
313 aNonSVGFrame
->IsSVGOuterSVGFrame());
315 nsIFrame
* firstFrame
=
316 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame
);
317 // 'r' is in "user space":
318 nsRect r
= (aUnionContinuations
)
319 ? GetPreEffectsInkOverflowUnion(
320 firstFrame
, nullptr, nsRect(),
321 GetOffsetToBoundingBox(firstFrame
), false)
322 : GetPreEffectsInkOverflow(firstFrame
, aNonSVGFrame
,
323 GetOffsetToBoundingBox(firstFrame
));
325 return nsLayoutUtils::RectToGfxRect(r
, AppUnitsPerCSSPixel());
328 // XXX Since we're called during reflow, this method is broken for frames with
329 // continuations. When we're called for a frame with continuations, we're
330 // called for each continuation in turn as it's reflowed. However, it isn't
331 // until the last continuation is reflowed that this method's
332 // GetOffsetToBoundingBox() and GetPreEffectsInkOverflowUnion() calls will
333 // obtain valid border boxes for all the continuations. As a result, we'll
334 // end up returning bogus post-filter ink overflow rects for all the prior
335 // continuations. Unfortunately, by the time the last continuation is
336 // reflowed, it's too late to go back and set and propagate the overflow
337 // rects on the previous continuations.
339 // The reason that we need to pass an override bbox to
340 // GetPreEffectsInkOverflowUnion rather than just letting it call into our
341 // GetSVGBBoxForNonSVGFrame method is because we get called by
342 // ComputeEffectsRect when it has been called with
343 // aStoreRectProperties set to false. In this case the pre-effects visual
344 // overflow rect that it has been passed may be different to that stored on
345 // aFrame, resulting in a different bbox.
347 // XXXjwatt The pre-effects ink overflow rect passed to
348 // ComputeEffectsRect won't include continuation overflows, so
349 // for frames with continuation the following filter analysis will likely end
350 // up being carried out with a bbox created as if the frame didn't have
353 // XXXjwatt Using aPreEffectsOverflowRect to create the bbox isn't really right
354 // for SVG frames, since for SVG frames the SVG spec defines the bbox to be
355 // something quite different to the pre-effects ink overflow rect. However,
356 // we're essentially calculating an invalidation area here, and using the
357 // pre-effects overflow rect will actually overestimate that area which, while
358 // being a bit wasteful, isn't otherwise a problem.
360 nsRect
SVGIntegrationUtils::ComputePostEffectsInkOverflowRect(
361 nsIFrame
* aFrame
, const nsRect
& aPreEffectsOverflowRect
) {
362 MOZ_ASSERT(!aFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
),
363 "Don't call this on SVG child frames");
365 MOZ_ASSERT(aFrame
->StyleEffects()->HasFilters(),
366 "We should only be called if the frame is filtered, since filters "
367 "are the only effect that affects overflow.");
369 nsIFrame
* firstFrame
=
370 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame
);
371 // Note: we do not return here for eHasNoRefs since we must still handle any
372 // CSS filter functions.
373 // TODO: We currently pass nullptr instead of an nsTArray* here, but we
374 // actually should get the filter frames and then pass them into
375 // GetPostFilterBounds below! See bug 1494263.
376 // TODO: we should really return an empty rect for eHasRefsSomeInvalid since
377 // in that case we disable painting of the element.
378 if (SVGObserverUtils::GetAndObserveFilters(firstFrame
, nullptr) ==
379 SVGObserverUtils::eHasRefsSomeInvalid
) {
380 return aPreEffectsOverflowRect
;
383 // Create an override bbox - see comment above:
384 nsPoint firstFrameToBoundingBox
= GetOffsetToBoundingBox(firstFrame
);
385 // overrideBBox is in "user space", in _CSS_ pixels:
386 // XXX Why are we rounding out to pixel boundaries? We don't do that in
387 // GetSVGBBoxForNonSVGFrame, and it doesn't appear to be necessary.
388 gfxRect overrideBBox
= nsLayoutUtils::RectToGfxRect(
389 GetPreEffectsInkOverflowUnion(firstFrame
, aFrame
, aPreEffectsOverflowRect
,
390 firstFrameToBoundingBox
, true),
391 AppUnitsPerCSSPixel());
392 overrideBBox
.RoundOut();
394 nsRect overflowRect
=
395 FilterInstance::GetPostFilterBounds(firstFrame
, &overrideBBox
);
397 // Return overflowRect relative to aFrame, rather than "user space":
398 return overflowRect
-
399 (aFrame
->GetOffsetTo(firstFrame
) + firstFrameToBoundingBox
);
402 nsIntRegion
SVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(
403 nsIFrame
* aFrame
, const nsPoint
& aToReferenceFrame
,
404 const nsIntRegion
& aInvalidRegion
) {
405 if (aInvalidRegion
.IsEmpty()) {
409 nsIFrame
* firstFrame
=
410 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame
);
412 // If we have any filters to observe then we should have started doing that
413 // during reflow/ComputeFrameEffectsRect, so we use GetFiltersIfObserving
414 // here to avoid needless work (or masking bugs by setting up observers at
416 if (!aFrame
->StyleEffects()->HasFilters() ||
417 SVGObserverUtils::GetFiltersIfObserving(firstFrame
, nullptr) ==
418 SVGObserverUtils::eHasRefsSomeInvalid
) {
419 return aInvalidRegion
;
422 int32_t appUnitsPerDevPixel
= aFrame
->PresContext()->AppUnitsPerDevPixel();
424 // Convert aInvalidRegion into bounding box frame space in app units:
425 nsPoint toBoundingBox
=
426 aFrame
->GetOffsetTo(firstFrame
) + GetOffsetToBoundingBox(firstFrame
);
427 // The initial rect was relative to the reference frame, so we need to
428 // remove that offset to get a rect relative to the current frame.
429 toBoundingBox
-= aToReferenceFrame
;
430 nsRegion preEffectsRegion
=
431 aInvalidRegion
.ToAppUnits(appUnitsPerDevPixel
).MovedBy(toBoundingBox
);
433 // Adjust the dirty area for effects, and shift it back to being relative to
434 // the reference frame.
436 FilterInstance::GetPostFilterDirtyArea(firstFrame
, preEffectsRegion
)
437 .MovedBy(-toBoundingBox
);
438 // Return the result, in pixels relative to the reference frame.
439 return result
.ToOutsidePixels(appUnitsPerDevPixel
);
442 nsRect
SVGIntegrationUtils::GetRequiredSourceForInvalidArea(
443 nsIFrame
* aFrame
, const nsRect
& aDirtyRect
) {
444 nsIFrame
* firstFrame
=
445 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame
);
447 // If we have any filters to observe then we should have started doing that
448 // during reflow/ComputeFrameEffectsRect, so we use GetFiltersIfObserving
449 // here to avoid needless work (or masking bugs by setting up observers at
451 if (!aFrame
->StyleEffects()->HasFilters() ||
452 SVGObserverUtils::GetFiltersIfObserving(firstFrame
, nullptr) ==
453 SVGObserverUtils::eHasRefsSomeInvalid
) {
457 // Convert aDirtyRect into "user space" in app units:
458 nsPoint toUserSpace
=
459 aFrame
->GetOffsetTo(firstFrame
) + GetOffsetToBoundingBox(firstFrame
);
460 nsRect postEffectsRect
= aDirtyRect
+ toUserSpace
;
462 // Return ther result, relative to aFrame, not in user space:
463 return FilterInstance::GetPreFilterNeededArea(firstFrame
, postEffectsRect
)
468 bool SVGIntegrationUtils::HitTestFrameForEffects(nsIFrame
* aFrame
,
469 const nsPoint
& aPt
) {
470 nsIFrame
* firstFrame
=
471 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame
);
472 // Convert aPt to user space:
474 if (aFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
)) {
475 // XXXmstange Isn't this wrong for svg:use and innerSVG frames?
476 toUserSpace
= aFrame
->GetPosition();
479 aFrame
->GetOffsetTo(firstFrame
) + GetOffsetToBoundingBox(firstFrame
);
481 nsPoint pt
= aPt
+ toUserSpace
;
482 gfxPoint userSpacePt
= gfxPoint(pt
.x
, pt
.y
) / AppUnitsPerCSSPixel();
483 return SVGUtils::HitTestClip(firstFrame
, userSpacePt
);
486 using PaintFramesParams
= SVGIntegrationUtils::PaintFramesParams
;
489 * Paint css-positioned-mask onto a given target(aMaskDT).
490 * Return value indicates if mask is complete.
492 static bool PaintMaskSurface(const PaintFramesParams
& aParams
,
493 DrawTarget
* aMaskDT
, float aOpacity
,
495 const nsTArray
<SVGMaskFrame
*>& aMaskFrames
,
496 const Matrix
& aMaskSurfaceMatrix
,
497 const nsPoint
& aOffsetToUserSpace
) {
498 MOZ_ASSERT(aMaskFrames
.Length() > 0);
499 MOZ_ASSERT(aMaskDT
->GetFormat() == SurfaceFormat::A8
);
500 MOZ_ASSERT(aOpacity
== 1.0 || aMaskFrames
.Length() == 1);
502 const nsStyleSVGReset
* svgReset
= aSC
->StyleSVGReset();
503 gfxMatrix cssPxToDevPxMatrix
= SVGUtils::GetCSSPxToDevPxMatrix(aParams
.frame
);
505 nsPresContext
* presContext
= aParams
.frame
->PresContext();
506 gfxPoint devPixelOffsetToUserSpace
= nsLayoutUtils::PointToGfxPoint(
507 aOffsetToUserSpace
, presContext
->AppUnitsPerDevPixel());
509 RefPtr
<gfxContext
> maskContext
=
510 gfxContext::CreatePreservingTransformOrNull(aMaskDT
);
511 MOZ_ASSERT(maskContext
);
513 bool isMaskComplete
= true;
515 // Multiple SVG masks interleave with image mask. Paint each layer onto
516 // aMaskDT one at a time.
517 for (int i
= aMaskFrames
.Length() - 1; i
>= 0; i
--) {
518 SVGMaskFrame
* maskFrame
= aMaskFrames
[i
];
519 CompositionOp compositionOp
=
520 (i
== int(aMaskFrames
.Length() - 1))
521 ? CompositionOp::OP_OVER
522 : nsCSSRendering::GetGFXCompositeMode(
523 svgReset
->mMask
.mLayers
[i
].mComposite
);
525 // maskFrame != nullptr means we get a SVG mask.
526 // maskFrame == nullptr means we get an image mask.
528 SVGMaskFrame::MaskParams
params(
529 maskContext
, aParams
.frame
, cssPxToDevPxMatrix
, aOpacity
,
530 svgReset
->mMask
.mLayers
[i
].mMaskMode
, aParams
.imgParams
);
531 RefPtr
<SourceSurface
> svgMask
= maskFrame
->GetMaskForMaskedFrame(params
);
533 Matrix tmp
= aMaskDT
->GetTransform();
534 aMaskDT
->SetTransform(Matrix());
535 aMaskDT
->MaskSurface(ColorPattern(DeviceColor(0.0, 0.0, 0.0, 1.0)),
536 svgMask
, Point(0, 0),
537 DrawOptions(1.0, compositionOp
));
538 aMaskDT
->SetTransform(tmp
);
540 } else if (svgReset
->mMask
.mLayers
[i
].mImage
.IsResolved()) {
541 gfxContextMatrixAutoSaveRestore
matRestore(maskContext
);
543 maskContext
->Multiply(gfxMatrix::Translation(-devPixelOffsetToUserSpace
));
544 nsCSSRendering::PaintBGParams params
=
545 nsCSSRendering::PaintBGParams::ForSingleLayer(
546 *presContext
, aParams
.dirtyRect
, aParams
.borderArea
,
548 aParams
.builder
->GetBackgroundPaintFlags() |
549 nsCSSRendering::PAINTBG_MASK_IMAGE
,
550 i
, compositionOp
, aOpacity
);
552 aParams
.imgParams
.result
&= nsCSSRendering::PaintStyleImageLayerWithSC(
553 params
, *maskContext
, aSC
, *aParams
.frame
->StyleBorder());
555 isMaskComplete
= false;
559 return isMaskComplete
;
562 struct MaskPaintResult
{
563 RefPtr
<SourceSurface
> maskSurface
;
564 Matrix maskTransform
;
565 bool transparentBlackMask
;
568 MaskPaintResult() : transparentBlackMask(false), opacityApplied(false) {}
571 static MaskPaintResult
CreateAndPaintMaskSurface(
572 const PaintFramesParams
& aParams
, float aOpacity
, ComputedStyle
* aSC
,
573 const nsTArray
<SVGMaskFrame
*>& aMaskFrames
,
574 const nsPoint
& aOffsetToUserSpace
) {
575 const nsStyleSVGReset
* svgReset
= aSC
->StyleSVGReset();
576 MOZ_ASSERT(aMaskFrames
.Length() > 0);
577 MaskPaintResult paintResult
;
579 gfxContext
& ctx
= aParams
.ctx
;
581 // Optimization for single SVG mask.
582 if (((aMaskFrames
.Length() == 1) && aMaskFrames
[0])) {
583 gfxMatrix cssPxToDevPxMatrix
=
584 SVGUtils::GetCSSPxToDevPxMatrix(aParams
.frame
);
585 paintResult
.opacityApplied
= true;
586 SVGMaskFrame::MaskParams
params(
587 &ctx
, aParams
.frame
, cssPxToDevPxMatrix
, aOpacity
,
588 svgReset
->mMask
.mLayers
[0].mMaskMode
, aParams
.imgParams
);
589 paintResult
.maskSurface
= aMaskFrames
[0]->GetMaskForMaskedFrame(params
);
590 paintResult
.maskTransform
= ctx
.CurrentMatrix();
591 paintResult
.maskTransform
.Invert();
592 if (!paintResult
.maskSurface
) {
593 paintResult
.transparentBlackMask
= true;
599 const Rect
& maskSurfaceRect
= aParams
.maskRect
.valueOr(Rect());
600 if (aParams
.maskRect
.isSome() && maskSurfaceRect
.IsEmpty()) {
601 // XXX: Is this ever true?
602 paintResult
.transparentBlackMask
= true;
606 RefPtr
<DrawTarget
> maskDT
= ctx
.GetDrawTarget()->CreateClippedDrawTarget(
607 maskSurfaceRect
, SurfaceFormat::A8
);
608 if (!maskDT
|| !maskDT
->IsValid()) {
612 // We can paint mask along with opacity only if
613 // 1. There is only one mask, or
614 // 2. No overlap among masks.
615 // Collision detect in #2 is not that trivial, we only accept #1 here.
616 paintResult
.opacityApplied
= (aMaskFrames
.Length() == 1);
618 // Set context's matrix on maskContext, offset by the maskSurfaceRect's
619 // position. This makes sure that we combine the masks in device space.
620 Matrix maskSurfaceMatrix
= ctx
.CurrentMatrix();
622 bool isMaskComplete
= PaintMaskSurface(
623 aParams
, maskDT
, paintResult
.opacityApplied
? aOpacity
: 1.0, aSC
,
624 aMaskFrames
, maskSurfaceMatrix
, aOffsetToUserSpace
);
626 if (!isMaskComplete
||
627 (aParams
.imgParams
.result
!= ImgDrawResult::SUCCESS
&&
628 aParams
.imgParams
.result
!= ImgDrawResult::SUCCESS_NOT_COMPLETE
&&
629 aParams
.imgParams
.result
!= ImgDrawResult::WRONG_SIZE
)) {
630 // Now we know the status of mask resource since we used it while painting.
631 // According to the return value of PaintMaskSurface, we know whether mask
632 // resource is resolvable or not.
635 // According to css-masking spec, always create a mask surface when
636 // we have any item in maskFrame even if all of those items are
637 // non-resolvable <mask-sources> or <images>.
638 // Set paintResult.transparentBlackMask as true, the caller should stop
639 // painting masked content as if this mask is a transparent black one.
641 // SVG 1.1 say that if we fail to resolve a mask, we should draw the
643 // Left paintResult.maskSurface empty, the caller should paint all
644 // masked content as if this mask is an opaque white one(no mask).
645 paintResult
.transparentBlackMask
=
646 !aParams
.frame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
);
648 MOZ_ASSERT(!paintResult
.maskSurface
);
652 paintResult
.maskTransform
= maskSurfaceMatrix
;
653 if (!paintResult
.maskTransform
.Invert()) {
657 paintResult
.maskSurface
= maskDT
->Snapshot();
661 static bool ValidateSVGFrame(nsIFrame
* aFrame
) {
663 NS_ASSERTION(!aFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
) ||
664 (NS_SVGDisplayListPaintingEnabled() &&
665 !aFrame
->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY
)),
666 "Should not use SVGIntegrationUtils on this SVG frame");
669 bool hasSVGLayout
= aFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
);
672 ISVGDisplayableFrame
* svgFrame
= do_QueryFrame(aFrame
);
673 MOZ_ASSERT(svgFrame
&& aFrame
->GetContent()->IsSVGElement(),
674 "A non-SVG frame carries NS_FRAME_SVG_LAYOUT flag?");
677 const nsIContent
* content
= aFrame
->GetContent();
678 if (!static_cast<const SVGElement
*>(content
)->HasValidDimensions()) {
679 // The SVG spec says not to draw _anything_
689 AutoPopGroup() : mContext(nullptr) {}
693 mContext
->PopGroupAndBlend();
697 void SetContext(gfxContext
* aContext
) { mContext
= aContext
; }
700 gfxContext
* mContext
;
703 bool SVGIntegrationUtils::PaintMask(const PaintFramesParams
& aParams
,
704 bool& aOutIsMaskComplete
) {
705 aOutIsMaskComplete
= true;
707 SVGUtils::MaskUsage maskUsage
;
708 SVGUtils::DetermineMaskUsage(aParams
.frame
, aParams
.handleOpacity
, maskUsage
);
709 if (!maskUsage
.shouldDoSomething()) {
713 nsIFrame
* frame
= aParams
.frame
;
714 if (!ValidateSVGFrame(frame
)) {
718 gfxContext
& ctx
= aParams
.ctx
;
719 RefPtr
<DrawTarget
> maskTarget
= ctx
.GetDrawTarget();
721 if (maskUsage
.shouldGenerateMaskLayer
&&
722 (maskUsage
.shouldGenerateClipMaskLayer
||
723 maskUsage
.shouldApplyClipPath
)) {
724 // We will paint both mask of positioned mask and clip-path into
727 // Create one extra draw target for drawing positioned mask, so that we do
728 // not have to copy the content of maskTarget before painting
729 // clip-path into it.
730 if (!maskTarget
->CanCreateSimilarDrawTarget(maskTarget
->GetSize(),
731 SurfaceFormat::A8
)) {
734 maskTarget
= maskTarget
->CreateSimilarDrawTarget(maskTarget
->GetSize(),
738 nsIFrame
* firstFrame
=
739 nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame
);
740 nsTArray
<SVGMaskFrame
*> maskFrames
;
741 // XXX check return value?
742 SVGObserverUtils::GetAndObserveMasks(firstFrame
, &maskFrames
);
744 AutoPopGroup autoPop
;
745 bool shouldPushOpacity
=
746 (maskUsage
.opacity
!= 1.0) && (maskFrames
.Length() != 1);
747 if (shouldPushOpacity
) {
748 ctx
.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA
, maskUsage
.opacity
);
749 autoPop
.SetContext(&ctx
);
752 gfxContextMatrixAutoSaveRestore matSR
;
754 // Paint clip-path-basic-shape onto ctx
755 gfxContextAutoSaveRestore basicShapeSR
;
756 if (maskUsage
.shouldApplyBasicShapeOrPath
) {
757 matSR
.SetContext(&ctx
);
759 MoveContextOriginToUserSpace(firstFrame
, aParams
);
761 basicShapeSR
.SetContext(&ctx
);
762 CSSClipPathInstance::ApplyBasicShapeOrPathClip(
763 ctx
, frame
, SVGUtils::GetCSSPxToDevPxMatrix(frame
));
764 if (!maskUsage
.shouldGenerateMaskLayer
) {
765 // Only have basic-shape clip-path effect. Fill clipped region by
767 ctx
.SetDeviceColor(DeviceColor::MaskOpaqueWhite());
774 // Paint mask onto ctx.
775 if (maskUsage
.shouldGenerateMaskLayer
) {
777 matSR
.SetContext(&ctx
);
779 EffectOffsets offsets
= MoveContextOriginToUserSpace(frame
, aParams
);
780 aOutIsMaskComplete
= PaintMaskSurface(
781 aParams
, maskTarget
, shouldPushOpacity
? 1.0 : maskUsage
.opacity
,
782 firstFrame
->Style(), maskFrames
, ctx
.CurrentMatrix(),
783 offsets
.offsetToUserSpace
);
786 // Paint clip-path onto ctx.
787 if (maskUsage
.shouldGenerateClipMaskLayer
|| maskUsage
.shouldApplyClipPath
) {
789 matSR
.SetContext(&ctx
);
791 MoveContextOriginToUserSpace(firstFrame
, aParams
);
792 Matrix clipMaskTransform
;
793 gfxMatrix cssPxToDevPxMatrix
= SVGUtils::GetCSSPxToDevPxMatrix(frame
);
795 SVGClipPathFrame
* clipPathFrame
;
796 // XXX check return value?
797 SVGObserverUtils::GetAndObserveClipPath(firstFrame
, &clipPathFrame
);
798 RefPtr
<SourceSurface
> maskSurface
=
799 maskUsage
.shouldGenerateMaskLayer
? maskTarget
->Snapshot() : nullptr;
800 clipPathFrame
->PaintClipMask(ctx
, frame
, cssPxToDevPxMatrix
, maskSurface
,
801 ctx
.CurrentMatrix());
808 void PaintMaskAndClipPathInternal(const PaintFramesParams
& aParams
,
809 const T
& aPaintChild
) {
810 MOZ_ASSERT(SVGIntegrationUtils::UsingMaskOrClipPathForFrame(aParams
.frame
),
811 "Should not use this method when no mask or clipPath effect"
814 /* SVG defines the following rendering model:
818 * 3. Apply clipping, masking, group opacity
820 * We handle #3 here and perform a couple of optimizations:
822 * + Use cairo's clipPath when representable natively (single object
825 * + Merge opacity and masking if both used together.
827 nsIFrame
* frame
= aParams
.frame
;
828 if (!ValidateSVGFrame(frame
)) {
832 SVGUtils::MaskUsage maskUsage
;
833 SVGUtils::DetermineMaskUsage(aParams
.frame
, aParams
.handleOpacity
, maskUsage
);
835 if (maskUsage
.opacity
== 0.0f
) {
839 gfxContext
& context
= aParams
.ctx
;
840 gfxContextMatrixAutoSaveRestore
matrixAutoSaveRestore(&context
);
842 nsIFrame
* firstFrame
=
843 nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame
);
845 SVGClipPathFrame
* clipPathFrame
;
846 // XXX check return value?
847 SVGObserverUtils::GetAndObserveClipPath(firstFrame
, &clipPathFrame
);
849 nsTArray
<SVGMaskFrame
*> maskFrames
;
850 // XXX check return value?
851 SVGObserverUtils::GetAndObserveMasks(firstFrame
, &maskFrames
);
853 gfxMatrix cssPxToDevPxMatrix
= SVGUtils::GetCSSPxToDevPxMatrix(frame
);
855 bool shouldGenerateMask
=
856 (maskUsage
.opacity
!= 1.0f
|| maskUsage
.shouldGenerateClipMaskLayer
||
857 maskUsage
.shouldGenerateMaskLayer
);
858 bool shouldPushMask
= false;
860 /* Check if we need to do additional operations on this child's
861 * rendering, which necessitates rendering into another surface. */
862 if (shouldGenerateMask
) {
863 gfxContextMatrixAutoSaveRestore matSR
;
865 Matrix maskTransform
;
866 RefPtr
<SourceSurface
> maskSurface
;
867 bool opacityApplied
= false;
869 if (maskUsage
.shouldGenerateMaskLayer
) {
870 matSR
.SetContext(&context
);
872 // For css-mask, we want to generate a mask for each continuation frame,
873 // so we setup context matrix by the position of the current frame,
874 // instead of the first continuation frame.
875 EffectOffsets offsets
= MoveContextOriginToUserSpace(frame
, aParams
);
876 MaskPaintResult paintResult
= CreateAndPaintMaskSurface(
877 aParams
, maskUsage
.opacity
, firstFrame
->Style(), maskFrames
,
878 offsets
.offsetToUserSpace
);
880 if (paintResult
.transparentBlackMask
) {
884 maskSurface
= paintResult
.maskSurface
;
886 shouldPushMask
= true;
887 // We want the mask to be untransformed so use the inverse of the
888 // current transform as the maskTransform to compensate.
889 maskTransform
= context
.CurrentMatrix();
890 maskTransform
.Invert();
892 opacityApplied
= paintResult
.opacityApplied
;
896 if (maskUsage
.shouldGenerateClipMaskLayer
) {
898 matSR
.SetContext(&context
);
900 MoveContextOriginToUserSpace(firstFrame
, aParams
);
901 RefPtr
<SourceSurface
> clipMaskSurface
= clipPathFrame
->GetClipMask(
902 context
, frame
, cssPxToDevPxMatrix
, maskSurface
, maskTransform
);
904 if (clipMaskSurface
) {
905 maskSurface
= clipMaskSurface
;
906 // We want the mask to be untransformed so use the inverse of the
907 // current transform as the maskTransform to compensate.
908 maskTransform
= context
.CurrentMatrix();
909 maskTransform
.Invert();
911 // Either entire surface is clipped out, or gfx buffer allocation
912 // failure in SVGClipPathFrame::GetClipMask.
916 shouldPushMask
= true;
920 if (!maskUsage
.shouldGenerateClipMaskLayer
&&
921 !maskUsage
.shouldGenerateMaskLayer
) {
922 MOZ_ASSERT(maskUsage
.opacity
!= 1.0f
);
924 matSR
.SetContext(&context
);
925 MoveContextOriginToUserSpace(firstFrame
, aParams
);
926 shouldPushMask
= true;
929 if (shouldPushMask
) {
930 context
.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA
,
931 opacityApplied
? 1.0 : maskUsage
.opacity
,
932 maskSurface
, maskTransform
);
936 /* If this frame has only a trivial clipPath, set up cairo's clipping now so
937 * we can just do normal painting and get it clipped appropriately.
939 if (maskUsage
.shouldApplyClipPath
|| maskUsage
.shouldApplyBasicShapeOrPath
) {
940 gfxContextMatrixAutoSaveRestore
matSR(&context
);
942 MoveContextOriginToUserSpace(firstFrame
, aParams
);
944 MOZ_ASSERT(!maskUsage
.shouldApplyClipPath
||
945 !maskUsage
.shouldApplyBasicShapeOrPath
);
946 if (maskUsage
.shouldApplyClipPath
) {
947 clipPathFrame
->ApplyClipPath(context
, frame
, cssPxToDevPxMatrix
);
949 CSSClipPathInstance::ApplyBasicShapeOrPathClip(context
, frame
,
954 /* Paint the child */
955 context
.SetMatrix(matrixAutoSaveRestore
.Matrix());
958 if (StaticPrefs::layers_draw_mask_debug()) {
959 gfxContextAutoSaveRestore
saver(&context
);
962 gfxRect drawingRect
= nsLayoutUtils::RectToGfxRect(
963 aParams
.borderArea
, frame
->PresContext()->AppUnitsPerDevPixel());
964 context
.SnappedRectangle(drawingRect
);
965 sRGBColor
overlayColor(0.0f
, 0.0f
, 0.0f
, 0.8f
);
966 if (maskUsage
.shouldGenerateMaskLayer
) {
967 overlayColor
.r
= 1.0f
; // red represents css positioned mask.
969 if (maskUsage
.shouldApplyClipPath
||
970 maskUsage
.shouldGenerateClipMaskLayer
) {
971 overlayColor
.g
= 1.0f
; // green represents clip-path:<clip-source>.
973 if (maskUsage
.shouldApplyBasicShapeOrPath
) {
974 overlayColor
.b
= 1.0f
; // blue represents
975 // clip-path:<basic-shape>||<geometry-box>.
978 context
.SetColor(overlayColor
);
982 if (maskUsage
.shouldApplyClipPath
|| maskUsage
.shouldApplyBasicShapeOrPath
) {
986 if (shouldPushMask
) {
987 context
.PopGroupAndBlend();
991 void SVGIntegrationUtils::PaintMaskAndClipPath(
992 const PaintFramesParams
& aParams
,
993 const std::function
<void()>& aPaintChild
) {
994 PaintMaskAndClipPathInternal(aParams
, aPaintChild
);
997 void SVGIntegrationUtils::PaintFilter(const PaintFramesParams
& aParams
,
998 const SVGFilterPaintCallback
& aCallback
) {
999 MOZ_ASSERT(!aParams
.builder
->IsForGenerateGlyphMask(),
1000 "Filter effect is discarded while generating glyph mask.");
1001 MOZ_ASSERT(aParams
.frame
->StyleEffects()->HasFilters(),
1002 "Should not use this method when no filter effect on this frame");
1004 nsIFrame
* frame
= aParams
.frame
;
1005 if (!ValidateSVGFrame(frame
)) {
1009 float opacity
= SVGUtils::ComputeOpacity(frame
, aParams
.handleOpacity
);
1010 if (opacity
== 0.0f
) {
1014 /* Properties are added lazily and may have been removed by a restyle,
1015 so make sure all applicable ones are set again. */
1016 nsIFrame
* firstFrame
=
1017 nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame
);
1018 // Note: we do not return here for eHasNoRefs since we must still handle any
1019 // CSS filter functions.
1020 // TODO: We currently pass nullptr instead of an nsTArray* here, but we
1021 // actually should get the filter frames and then pass them into
1022 // PaintFilteredFrame below! See bug 1494263.
1023 // XXX: Do we need to check for eHasRefsSomeInvalid here given that
1024 // nsDisplayFilter::BuildLayer returns nullptr for eHasRefsSomeInvalid?
1025 // Or can we just assert !eHasRefsSomeInvalid?
1026 if (SVGObserverUtils::GetAndObserveFilters(firstFrame
, nullptr) ==
1027 SVGObserverUtils::eHasRefsSomeInvalid
) {
1031 gfxContext
& context
= aParams
.ctx
;
1033 gfxContextAutoSaveRestore
autoSR(&context
);
1034 EffectOffsets offsets
= MoveContextOriginToUserSpace(firstFrame
, aParams
);
1036 /* Paint the child and apply filters */
1037 nsRegion dirtyRegion
= aParams
.dirtyRect
- offsets
.offsetToBoundingBox
;
1039 FilterInstance::PaintFilteredFrame(frame
, &context
, aCallback
, &dirtyRegion
,
1040 aParams
.imgParams
, opacity
);
1043 bool SVGIntegrationUtils::CreateWebRenderCSSFilters(
1044 Span
<const StyleFilter
> aFilters
, nsIFrame
* aFrame
,
1045 WrFiltersHolder
& aWrFilters
) {
1046 // All CSS filters are supported by WebRender. SVG filters are not fully
1047 // supported, those use NS_STYLE_FILTER_URL and are handled separately.
1049 // If there are too many filters to render, then just pretend that we
1050 // succeeded, and don't render any of them.
1051 if (aFilters
.Length() >
1052 StaticPrefs::gfx_webrender_max_filter_ops_per_chain()) {
1055 aWrFilters
.filters
.SetCapacity(aFilters
.Length());
1056 auto& wrFilters
= aWrFilters
.filters
;
1057 for (const StyleFilter
& filter
: aFilters
) {
1058 switch (filter
.tag
) {
1059 case StyleFilter::Tag::Brightness
:
1060 wrFilters
.AppendElement(
1061 wr::FilterOp::Brightness(filter
.AsBrightness()));
1063 case StyleFilter::Tag::Contrast
:
1064 wrFilters
.AppendElement(wr::FilterOp::Contrast(filter
.AsContrast()));
1066 case StyleFilter::Tag::Grayscale
:
1067 wrFilters
.AppendElement(wr::FilterOp::Grayscale(filter
.AsGrayscale()));
1069 case StyleFilter::Tag::Invert
:
1070 wrFilters
.AppendElement(wr::FilterOp::Invert(filter
.AsInvert()));
1072 case StyleFilter::Tag::Opacity
: {
1073 float opacity
= filter
.AsOpacity();
1074 wrFilters
.AppendElement(wr::FilterOp::Opacity(
1075 wr::PropertyBinding
<float>::Value(opacity
), opacity
));
1078 case StyleFilter::Tag::Saturate
:
1079 wrFilters
.AppendElement(wr::FilterOp::Saturate(filter
.AsSaturate()));
1081 case StyleFilter::Tag::Sepia
:
1082 wrFilters
.AppendElement(wr::FilterOp::Sepia(filter
.AsSepia()));
1084 case StyleFilter::Tag::HueRotate
: {
1085 wrFilters
.AppendElement(
1086 wr::FilterOp::HueRotate(filter
.AsHueRotate().ToDegrees()));
1089 case StyleFilter::Tag::Blur
: {
1090 // TODO(emilio): we should go directly from css pixels -> device pixels.
1091 float appUnitsPerDevPixel
=
1092 aFrame
->PresContext()->AppUnitsPerDevPixel();
1093 float radius
= NSAppUnitsToFloatPixels(filter
.AsBlur().ToAppUnits(),
1094 appUnitsPerDevPixel
);
1095 wrFilters
.AppendElement(wr::FilterOp::Blur(radius
, radius
));
1098 case StyleFilter::Tag::DropShadow
: {
1099 float appUnitsPerDevPixel
=
1100 aFrame
->PresContext()->AppUnitsPerDevPixel();
1101 const StyleSimpleShadow
& shadow
= filter
.AsDropShadow();
1102 nscolor color
= shadow
.color
.CalcColor(aFrame
);
1104 wr::Shadow wrShadow
;
1106 NSAppUnitsToFloatPixels(shadow
.horizontal
.ToAppUnits(),
1107 appUnitsPerDevPixel
),
1108 NSAppUnitsToFloatPixels(shadow
.vertical
.ToAppUnits(),
1109 appUnitsPerDevPixel
)};
1110 wrShadow
.blur_radius
= NSAppUnitsToFloatPixels(shadow
.blur
.ToAppUnits(),
1111 appUnitsPerDevPixel
);
1112 wrShadow
.color
= {NS_GET_R(color
) / 255.0f
, NS_GET_G(color
) / 255.0f
,
1113 NS_GET_B(color
) / 255.0f
, NS_GET_A(color
) / 255.0f
};
1114 wrFilters
.AppendElement(wr::FilterOp::DropShadow(wrShadow
));
1125 bool SVGIntegrationUtils::BuildWebRenderFilters(
1126 nsIFrame
* aFilteredFrame
, Span
<const StyleFilter
> aFilters
,
1127 WrFiltersHolder
& aWrFilters
, Maybe
<nsRect
>& aPostFilterClip
,
1128 bool& aInitialized
) {
1129 return FilterInstance::BuildWebRenderFilters(
1130 aFilteredFrame
, aFilters
, aWrFilters
, aPostFilterClip
, aInitialized
);
1133 bool SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(nsIFrame
* aFrame
) {
1134 WrFiltersHolder wrFilters
;
1135 Maybe
<nsRect
> filterClip
;
1136 auto filterChain
= aFrame
->StyleEffects()->mFilters
.AsSpan();
1137 bool initialized
= true;
1138 return CreateWebRenderCSSFilters(filterChain
, aFrame
, wrFilters
) ||
1139 BuildWebRenderFilters(aFrame
, filterChain
, wrFilters
, filterClip
,
1143 bool SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(
1145 // WebRender supports masks / clip-paths and some filters in the compositor.
1146 // Non-WebRender doesn't support any SVG effects in the compositor.
1147 if (aFrame
->StyleEffects()->HasFilters()) {
1148 return !gfx::gfxVars::UseWebRender() ||
1149 !SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(aFrame
);
1151 if (SVGIntegrationUtils::UsingMaskOrClipPathForFrame(aFrame
)) {
1152 return !gfx::gfxVars::UseWebRender();
1157 class PaintFrameCallback
: public gfxDrawingCallback
{
1159 PaintFrameCallback(nsIFrame
* aFrame
, const nsSize aPaintServerSize
,
1160 const IntSize aRenderSize
, uint32_t aFlags
)
1162 mPaintServerSize(aPaintServerSize
),
1163 mRenderSize(aRenderSize
),
1165 virtual bool operator()(gfxContext
* aContext
, const gfxRect
& aFillRect
,
1166 const SamplingFilter aSamplingFilter
,
1167 const gfxMatrix
& aTransform
) override
;
1171 nsSize mPaintServerSize
;
1172 IntSize mRenderSize
;
1176 bool PaintFrameCallback::operator()(gfxContext
* aContext
,
1177 const gfxRect
& aFillRect
,
1178 const SamplingFilter aSamplingFilter
,
1179 const gfxMatrix
& aTransform
) {
1180 if (mFrame
->HasAnyStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER
)) {
1184 AutoSetRestorePaintServerState
paintServer(mFrame
);
1188 // Clip to aFillRect so that we don't paint outside.
1189 aContext
->NewPath();
1190 aContext
->Rectangle(aFillRect
);
1193 gfxMatrix invmatrix
= aTransform
;
1194 if (!invmatrix
.Invert()) {
1197 aContext
->Multiply(invmatrix
);
1199 // nsLayoutUtils::PaintFrame will anchor its painting at mFrame. But we want
1200 // to have it anchored at the top left corner of the bounding box of all of
1201 // mFrame's continuations. So we add a translation transform.
1202 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
1203 nsPoint offset
= SVGIntegrationUtils::GetOffsetToBoundingBox(mFrame
);
1204 gfxPoint devPxOffset
= gfxPoint(offset
.x
, offset
.y
) / appUnitsPerDevPixel
;
1205 aContext
->Multiply(gfxMatrix::Translation(devPxOffset
));
1207 gfxSize paintServerSize
=
1208 gfxSize(mPaintServerSize
.width
, mPaintServerSize
.height
) /
1209 mFrame
->PresContext()->AppUnitsPerDevPixel();
1211 // nsLayoutUtils::PaintFrame wants to render with paintServerSize, but we
1212 // want it to render with mRenderSize, so we need to set up a scale transform.
1213 gfxFloat scaleX
= mRenderSize
.width
/ paintServerSize
.width
;
1214 gfxFloat scaleY
= mRenderSize
.height
/ paintServerSize
.height
;
1215 aContext
->Multiply(gfxMatrix::Scaling(scaleX
, scaleY
));
1218 nsRect
dirty(-offset
.x
, -offset
.y
, mPaintServerSize
.width
,
1219 mPaintServerSize
.height
);
1221 using PaintFrameFlags
= nsLayoutUtils::PaintFrameFlags
;
1222 PaintFrameFlags flags
= PaintFrameFlags::InTransform
;
1223 if (mFlags
& SVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES
) {
1224 flags
|= PaintFrameFlags::SyncDecodeImages
;
1226 nsLayoutUtils::PaintFrame(aContext
, mFrame
, dirty
, NS_RGBA(0, 0, 0, 0),
1227 nsDisplayListBuilderMode::Painting
, flags
);
1229 nsIFrame
* currentFrame
= mFrame
;
1230 while ((currentFrame
= currentFrame
->GetNextContinuation()) != nullptr) {
1231 offset
= currentFrame
->GetOffsetToCrossDoc(mFrame
);
1232 devPxOffset
= gfxPoint(offset
.x
, offset
.y
) / appUnitsPerDevPixel
;
1235 aContext
->Multiply(gfxMatrix::Scaling(1 / scaleX
, 1 / scaleY
));
1236 aContext
->Multiply(gfxMatrix::Translation(devPxOffset
));
1237 aContext
->Multiply(gfxMatrix::Scaling(scaleX
, scaleY
));
1239 nsLayoutUtils::PaintFrame(aContext
, currentFrame
, dirty
- offset
,
1240 NS_RGBA(0, 0, 0, 0),
1241 nsDisplayListBuilderMode::Painting
, flags
);
1243 aContext
->Restore();
1246 aContext
->Restore();
1252 already_AddRefed
<gfxDrawable
> SVGIntegrationUtils::DrawableFromPaintServer(
1253 nsIFrame
* aFrame
, nsIFrame
* aTarget
, const nsSize
& aPaintServerSize
,
1254 const IntSize
& aRenderSize
, const DrawTarget
* aDrawTarget
,
1255 const gfxMatrix
& aContextMatrix
, uint32_t aFlags
) {
1256 // aPaintServerSize is the size that would be filled when using
1257 // background-repeat:no-repeat and background-size:auto. For normal background
1258 // images, this would be the intrinsic size of the image; for gradients and
1259 // patterns this would be the whole target frame fill area.
1260 // aRenderSize is what we will be actually filling after accounting for
1262 if (SVGPaintServerFrame
* server
= do_QueryFrame(aFrame
)) {
1263 // aFrame is either a pattern or a gradient. These fill the whole target
1264 // frame by default, so aPaintServerSize is the whole target background fill
1266 gfxRect
overrideBounds(0, 0, aPaintServerSize
.width
,
1267 aPaintServerSize
.height
);
1268 overrideBounds
.Scale(1.0 / aFrame
->PresContext()->AppUnitsPerDevPixel());
1269 imgDrawingParams
imgParams(aFlags
);
1270 RefPtr
<gfxPattern
> pattern
= server
->GetPaintServerPattern(
1271 aTarget
, aDrawTarget
, aContextMatrix
, &nsStyleSVG::mFill
, 1.0,
1272 imgParams
, &overrideBounds
);
1278 // pattern is now set up to fill aPaintServerSize. But we want it to
1279 // fill aRenderSize, so we need to add a scaling transform.
1280 // We couldn't just have set overrideBounds to aRenderSize - it would have
1281 // worked for gradients, but for patterns it would result in a different
1283 gfxFloat scaleX
= overrideBounds
.Width() / aRenderSize
.width
;
1284 gfxFloat scaleY
= overrideBounds
.Height() / aRenderSize
.height
;
1285 gfxMatrix scaleMatrix
= gfxMatrix::Scaling(scaleX
, scaleY
);
1286 pattern
->SetMatrix(scaleMatrix
* pattern
->GetMatrix());
1287 RefPtr
<gfxDrawable
> drawable
= new gfxPatternDrawable(pattern
, aRenderSize
);
1288 return drawable
.forget();
1291 if (aFrame
->IsFrameOfType(nsIFrame::eSVG
) &&
1292 !static_cast<ISVGDisplayableFrame
*>(do_QueryFrame(aFrame
))) {
1293 MOZ_ASSERT_UNREACHABLE(
1294 "We should prevent painting of unpaintable SVG "
1295 "before we get here");
1299 // We don't want to paint into a surface as long as we don't need to, so we
1300 // set up a drawing callback.
1301 RefPtr
<gfxDrawingCallback
> cb
=
1302 new PaintFrameCallback(aFrame
, aPaintServerSize
, aRenderSize
, aFlags
);
1303 RefPtr
<gfxDrawable
> drawable
= new gfxCallbackDrawable(cb
, aRenderSize
);
1304 return drawable
.forget();
1307 } // namespace mozilla