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"
13 #include "nsCSSAnonBoxes.h"
14 #include "nsCSSRendering.h"
15 #include "nsDisplayList.h"
16 #include "nsLayoutUtils.h"
17 #include "gfxContext.h"
18 #include "SVGPaintServerFrame.h"
19 #include "mozilla/gfx/Point.h"
20 #include "mozilla/CSSClipPathInstance.h"
21 #include "mozilla/FilterInstance.h"
22 #include "mozilla/StaticPrefs_layers.h"
23 #include "mozilla/SVGClipPathFrame.h"
24 #include "mozilla/SVGObserverUtils.h"
25 #include "mozilla/SVGMaskFrame.h"
26 #include "mozilla/SVGUtils.h"
27 #include "mozilla/Unused.h"
28 #include "mozilla/dom/SVGElement.h"
30 using namespace mozilla::dom
;
31 using namespace mozilla::layers
;
32 using namespace mozilla::gfx
;
33 using namespace mozilla::image
;
38 * This class is used to get the pre-effects ink overflow rect of a frame,
39 * or, in the case of a frame with continuations, to collect the union of the
40 * pre-effects ink overflow rects of all the continuations. The result is
41 * relative to the origin (top left corner of the border box) of the frame, or,
42 * if the frame has continuations, the origin of the _first_ continuation.
44 class PreEffectsInkOverflowCollector
: public nsLayoutUtils::BoxCallback
{
47 * If the pre-effects ink overflow rect of the frame being examined
48 * happens to be known, it can be passed in as aCurrentFrame and its
49 * pre-effects ink overflow rect can be passed in as
50 * aCurrentFrameOverflowArea. This is just an optimization to save a
51 * frame property lookup - these arguments are optional.
53 PreEffectsInkOverflowCollector(nsIFrame
* aFirstContinuation
,
54 nsIFrame
* aCurrentFrame
,
55 const nsRect
& aCurrentFrameOverflowArea
,
57 : mFirstContinuation(aFirstContinuation
),
58 mCurrentFrame(aCurrentFrame
),
59 mCurrentFrameOverflowArea(aCurrentFrameOverflowArea
),
60 mInReflow(aInReflow
) {
61 NS_ASSERTION(!mFirstContinuation
->GetPrevContinuation(),
62 "We want the first continuation here");
65 void AddBox(nsIFrame
* aFrame
) override
{
66 nsRect overflow
= (aFrame
== mCurrentFrame
)
67 ? mCurrentFrameOverflowArea
68 : PreEffectsInkOverflowRect(aFrame
, mInReflow
);
69 mResult
.UnionRect(mResult
,
70 overflow
+ aFrame
->GetOffsetTo(mFirstContinuation
));
73 nsRect
GetResult() const { return mResult
; }
76 static nsRect
PreEffectsInkOverflowRect(nsIFrame
* aFrame
, bool aInReflow
) {
77 nsRect
* r
= aFrame
->GetProperty(nsIFrame::PreEffectsBBoxProperty());
83 // Having PreTransformOverflowAreasProperty cached means
84 // InkOverflowRect() will return post-effect rect, which is not what
85 // we want. This function intentional reports pre-effect rect. But it does
86 // not matter if there is no SVG effect on this frame, since no effect
87 // means post-effect rect matches pre-effect rect.
89 // This function may be called during reflow or painting. We should only
90 // do this check in painting process since the PreEffectsBBoxProperty of
91 // continuations are not set correctly while reflowing.
92 if (SVGIntegrationUtils::UsingOverflowAffectingEffects(aFrame
) &&
94 OverflowAreas
* preTransformOverflows
=
95 aFrame
->GetProperty(nsIFrame::PreTransformOverflowAreasProperty());
97 MOZ_ASSERT(!preTransformOverflows
,
98 "InkOverflowRect() won't return the pre-effects rect!");
101 return aFrame
->InkOverflowRectRelativeToSelf();
104 nsIFrame
* mFirstContinuation
;
105 nsIFrame
* mCurrentFrame
;
106 const nsRect
& mCurrentFrameOverflowArea
;
112 * Gets the union of the pre-effects ink overflow rects of all of a frame's
113 * continuations, in "user space".
115 static nsRect
GetPreEffectsInkOverflowUnion(
116 nsIFrame
* aFirstContinuation
, nsIFrame
* aCurrentFrame
,
117 const nsRect
& aCurrentFramePreEffectsOverflow
,
118 const nsPoint
& aFirstContinuationToUserSpace
, bool aInReflow
) {
119 NS_ASSERTION(!aFirstContinuation
->GetPrevContinuation(),
120 "Need first continuation here");
121 PreEffectsInkOverflowCollector
collector(aFirstContinuation
, aCurrentFrame
,
122 aCurrentFramePreEffectsOverflow
,
124 // Compute union of all overflow areas relative to aFirstContinuation:
125 nsLayoutUtils::GetAllInFlowBoxes(aFirstContinuation
, &collector
);
126 // Return the result in user space:
127 return collector
.GetResult() + aFirstContinuationToUserSpace
;
131 * Gets the pre-effects ink overflow rect of aCurrentFrame in "user space".
133 static nsRect
GetPreEffectsInkOverflow(
134 nsIFrame
* aFirstContinuation
, nsIFrame
* aCurrentFrame
,
135 const nsPoint
& aFirstContinuationToUserSpace
) {
136 NS_ASSERTION(!aFirstContinuation
->GetPrevContinuation(),
137 "Need first continuation here");
138 PreEffectsInkOverflowCollector
collector(aFirstContinuation
, nullptr,
140 // Compute overflow areas of current frame relative to aFirstContinuation:
141 nsLayoutUtils::AddBoxesForFrame(aCurrentFrame
, &collector
);
142 // Return the result in user space:
143 return collector
.GetResult() + aFirstContinuationToUserSpace
;
146 bool SVGIntegrationUtils::UsingOverflowAffectingEffects(
147 const nsIFrame
* aFrame
) {
148 // Currently overflow don't take account of SVG or other non-absolute
149 // positioned clipping, or masking.
150 return aFrame
->StyleEffects()->HasFilters();
153 bool SVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame
* aFrame
) {
154 // Even when SVG display lists are disabled, returning true for SVG frames
155 // does not adversely affect any of our callers. Therefore we don't bother
156 // checking the SDL prefs here, since we don't know if we're being called for
157 // painting or hit-testing anyway.
158 const nsStyleSVGReset
* style
= aFrame
->StyleSVGReset();
159 const nsStyleEffects
* effects
= aFrame
->StyleEffects();
160 // TODO(cbrewster): remove backdrop-filter from this list once it is supported
161 // in preserve-3d cases.
162 return effects
->HasFilters() || effects
->HasBackdropFilters() ||
163 style
->HasClipPath() || style
->HasMask();
166 nsPoint
SVGIntegrationUtils::GetOffsetToBoundingBox(nsIFrame
* aFrame
) {
167 if (aFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
)) {
168 // Do NOT call GetAllInFlowRectsUnion for SVG - it will get the
169 // covered region relative to the SVGOuterSVGFrame, which is absolutely
170 // not what we want. SVG frames are always in user space, so they have
171 // no offset adjustment to make.
175 // The GetAllInFlowRectsUnion() call gets the union of the frame border-box
176 // rects over all continuations, relative to the origin (top-left of the
177 // border box) of its second argument (here, aFrame, the first continuation).
178 return -nsLayoutUtils::GetAllInFlowRectsUnion(aFrame
, aFrame
).TopLeft();
181 struct EffectOffsets
{
182 // The offset between the reference frame and the bounding box of the
183 // target frame in app unit.
184 nsPoint offsetToBoundingBox
;
185 // The offset between the reference frame and the bounding box of the
186 // target frame in app unit.
187 nsPoint offsetToUserSpace
;
188 // The offset between the reference frame and the bounding box of the
189 // target frame in device unit.
190 gfxPoint offsetToUserSpaceInDevPx
;
193 static EffectOffsets
ComputeEffectOffset(
194 nsIFrame
* aFrame
, const SVGIntegrationUtils::PaintFramesParams
& aParams
) {
195 EffectOffsets result
;
197 result
.offsetToBoundingBox
=
198 aParams
.builder
->ToReferenceFrame(aFrame
) -
199 SVGIntegrationUtils::GetOffsetToBoundingBox(aFrame
);
200 if (!aFrame
->IsSVGFrame()) {
201 /* Snap the offset if the reference frame is not a SVG frame,
202 * since other frames will be snapped to pixel when rendering. */
203 result
.offsetToBoundingBox
=
204 nsPoint(aFrame
->PresContext()->RoundAppUnitsToNearestDevPixels(
205 result
.offsetToBoundingBox
.x
),
206 aFrame
->PresContext()->RoundAppUnitsToNearestDevPixels(
207 result
.offsetToBoundingBox
.y
));
210 // After applying only "aOffsetToBoundingBox", aParams.ctx would have its
211 // origin at the top left corner of frame's bounding box (over all
213 // However, SVG painting needs the origin to be located at the origin of the
214 // SVG frame's "user space", i.e. the space in which, for example, the
215 // frame's BBox lives.
216 // SVG geometry frames and foreignObject frames apply their own offsets, so
217 // their position is relative to their user space. So for these frame types,
218 // if we want aParams.ctx to be in user space, we first need to subtract the
219 // frame's position so that SVG painting can later add it again and the
220 // frame is painted in the right place.
221 gfxPoint toUserSpaceGfx
=
222 SVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame
);
223 nsPoint toUserSpace
=
224 nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx
.x
)),
225 nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx
.y
)));
227 result
.offsetToUserSpace
= result
.offsetToBoundingBox
- toUserSpace
;
230 bool hasSVGLayout
= aFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
);
232 hasSVGLayout
|| result
.offsetToBoundingBox
== result
.offsetToUserSpace
,
233 "For non-SVG frames there shouldn't be any additional offset");
236 result
.offsetToUserSpaceInDevPx
= nsLayoutUtils::PointToGfxPoint(
237 result
.offsetToUserSpace
, aFrame
->PresContext()->AppUnitsPerDevPixel());
243 * Setup transform matrix of a gfx context by a specific frame. Move the
244 * origin of aParams.ctx to the user space of aFrame.
246 static EffectOffsets
MoveContextOriginToUserSpace(
247 nsIFrame
* aFrame
, const SVGIntegrationUtils::PaintFramesParams
& aParams
) {
248 EffectOffsets offset
= ComputeEffectOffset(aFrame
, aParams
);
250 aParams
.ctx
.SetMatrixDouble(aParams
.ctx
.CurrentMatrixDouble().PreTranslate(
251 offset
.offsetToUserSpaceInDevPx
));
256 gfxPoint
SVGIntegrationUtils::GetOffsetToUserSpaceInDevPx(
257 nsIFrame
* aFrame
, const PaintFramesParams
& aParams
) {
258 nsIFrame
* firstFrame
=
259 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame
);
260 EffectOffsets offset
= ComputeEffectOffset(firstFrame
, aParams
);
261 return offset
.offsetToUserSpaceInDevPx
;
265 nsSize
SVGIntegrationUtils::GetContinuationUnionSize(nsIFrame
* aNonSVGFrame
) {
266 NS_ASSERTION(!aNonSVGFrame
->IsSVGFrame(), "SVG frames should not get here");
267 nsIFrame
* firstFrame
=
268 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame
);
269 return nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame
, firstFrame
).Size();
272 /* static */ gfx::Size
SVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(
273 nsIFrame
* aNonSVGFrame
) {
274 NS_ASSERTION(!aNonSVGFrame
->IsSVGFrame(), "SVG frames should not get here");
275 nsIFrame
* firstFrame
=
276 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame
);
277 nsRect r
= nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame
, firstFrame
);
278 return gfx::Size(nsPresContext::AppUnitsToFloatCSSPixels(r
.width
),
279 nsPresContext::AppUnitsToFloatCSSPixels(r
.height
));
282 gfxRect
SVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(
283 nsIFrame
* aNonSVGFrame
, bool aUnionContinuations
) {
284 // Except for SVGOuterSVGFrame, we shouldn't be getting here with SVG
285 // frames at all. This function is for elements that are laid out using the
286 // CSS box model rules.
287 NS_ASSERTION(!aNonSVGFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
),
288 "Frames with SVG layout should not get here");
290 nsIFrame
* firstFrame
=
291 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame
);
292 // 'r' is in "user space":
293 nsRect r
= (aUnionContinuations
)
294 ? GetPreEffectsInkOverflowUnion(
295 firstFrame
, nullptr, nsRect(),
296 GetOffsetToBoundingBox(firstFrame
), false)
297 : GetPreEffectsInkOverflow(firstFrame
, aNonSVGFrame
,
298 GetOffsetToBoundingBox(firstFrame
));
300 return nsLayoutUtils::RectToGfxRect(r
, AppUnitsPerCSSPixel());
303 // XXX Since we're called during reflow, this method is broken for frames with
304 // continuations. When we're called for a frame with continuations, we're
305 // called for each continuation in turn as it's reflowed. However, it isn't
306 // until the last continuation is reflowed that this method's
307 // GetOffsetToBoundingBox() and GetPreEffectsInkOverflowUnion() calls will
308 // obtain valid border boxes for all the continuations. As a result, we'll
309 // end up returning bogus post-filter ink overflow rects for all the prior
310 // continuations. Unfortunately, by the time the last continuation is
311 // reflowed, it's too late to go back and set and propagate the overflow
312 // rects on the previous continuations.
314 // The reason that we need to pass an override bbox to
315 // GetPreEffectsInkOverflowUnion rather than just letting it call into our
316 // GetSVGBBoxForNonSVGFrame method is because we get called by
317 // ComputeEffectsRect when it has been called with
318 // aStoreRectProperties set to false. In this case the pre-effects visual
319 // overflow rect that it has been passed may be different to that stored on
320 // aFrame, resulting in a different bbox.
322 // XXXjwatt The pre-effects ink overflow rect passed to
323 // ComputeEffectsRect won't include continuation overflows, so
324 // for frames with continuation the following filter analysis will likely end
325 // up being carried out with a bbox created as if the frame didn't have
328 // XXXjwatt Using aPreEffectsOverflowRect to create the bbox isn't really right
329 // for SVG frames, since for SVG frames the SVG spec defines the bbox to be
330 // something quite different to the pre-effects ink overflow rect. However,
331 // we're essentially calculating an invalidation area here, and using the
332 // pre-effects overflow rect will actually overestimate that area which, while
333 // being a bit wasteful, isn't otherwise a problem.
335 nsRect
SVGIntegrationUtils::ComputePostEffectsInkOverflowRect(
336 nsIFrame
* aFrame
, const nsRect
& aPreEffectsOverflowRect
) {
337 MOZ_ASSERT(!aFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
),
338 "Don't call this on SVG child frames");
340 MOZ_ASSERT(aFrame
->StyleEffects()->HasFilters(),
341 "We should only be called if the frame is filtered, since filters "
342 "are the only effect that affects overflow.");
344 nsIFrame
* firstFrame
=
345 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame
);
346 // Note: we do not return here for eHasNoRefs since we must still handle any
347 // CSS filter functions.
348 // TODO: we should really return an empty rect for eHasRefsSomeInvalid since
349 // in that case we disable painting of the element.
350 nsTArray
<SVGFilterFrame
*> filterFrames
;
351 if (SVGObserverUtils::GetAndObserveFilters(firstFrame
, &filterFrames
) ==
352 SVGObserverUtils::eHasRefsSomeInvalid
) {
353 return aPreEffectsOverflowRect
;
356 // Create an override bbox - see comment above:
357 nsPoint firstFrameToBoundingBox
= GetOffsetToBoundingBox(firstFrame
);
358 // overrideBBox is in "user space", in _CSS_ pixels:
359 // XXX Why are we rounding out to pixel boundaries? We don't do that in
360 // GetSVGBBoxForNonSVGFrame, and it doesn't appear to be necessary.
361 gfxRect overrideBBox
= nsLayoutUtils::RectToGfxRect(
362 GetPreEffectsInkOverflowUnion(firstFrame
, aFrame
, aPreEffectsOverflowRect
,
363 firstFrameToBoundingBox
, true),
364 AppUnitsPerCSSPixel());
365 overrideBBox
.RoundOut();
367 Maybe
<nsRect
> overflowRect
= FilterInstance::GetPostFilterBounds(
368 firstFrame
, filterFrames
, &overrideBBox
);
370 return aPreEffectsOverflowRect
;
373 // Return overflowRect relative to aFrame, rather than "user space":
374 return overflowRect
.value() -
375 (aFrame
->GetOffsetTo(firstFrame
) + firstFrameToBoundingBox
);
378 nsRect
SVGIntegrationUtils::GetRequiredSourceForInvalidArea(
379 nsIFrame
* aFrame
, const nsRect
& aDirtyRect
) {
380 nsIFrame
* firstFrame
=
381 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame
);
383 // If we have any filters to observe then we should have started doing that
384 // during reflow/ComputeFrameEffectsRect, so we use GetFiltersIfObserving
385 // here to avoid needless work (or masking bugs by setting up observers at
387 nsTArray
<SVGFilterFrame
*> filterFrames
;
388 if (!aFrame
->StyleEffects()->HasFilters() ||
389 SVGObserverUtils::GetFiltersIfObserving(firstFrame
, &filterFrames
) ==
390 SVGObserverUtils::eHasRefsSomeInvalid
) {
394 // Convert aDirtyRect into "user space" in app units:
395 nsPoint toUserSpace
=
396 aFrame
->GetOffsetTo(firstFrame
) + GetOffsetToBoundingBox(firstFrame
);
397 nsRect postEffectsRect
= aDirtyRect
+ toUserSpace
;
399 // Return ther result, relative to aFrame, not in user space:
400 return FilterInstance::GetPreFilterNeededArea(firstFrame
, filterFrames
,
406 bool SVGIntegrationUtils::HitTestFrameForEffects(nsIFrame
* aFrame
,
407 const nsPoint
& aPt
) {
408 nsIFrame
* firstFrame
=
409 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame
);
410 // Convert aPt to user space:
412 if (aFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
)) {
413 // XXXmstange Isn't this wrong for svg:use and innerSVG frames?
414 toUserSpace
= aFrame
->GetPosition();
417 aFrame
->GetOffsetTo(firstFrame
) + GetOffsetToBoundingBox(firstFrame
);
419 nsPoint pt
= aPt
+ toUserSpace
;
420 gfxPoint userSpacePt
= gfxPoint(pt
.x
, pt
.y
) / AppUnitsPerCSSPixel();
421 return SVGUtils::HitTestClip(firstFrame
, userSpacePt
);
424 using PaintFramesParams
= SVGIntegrationUtils::PaintFramesParams
;
427 * Paint css-positioned-mask onto a given target(aMaskDT).
428 * Return value indicates if mask is complete.
430 static bool PaintMaskSurface(const PaintFramesParams
& aParams
,
431 DrawTarget
* aMaskDT
, float aOpacity
,
432 const ComputedStyle
* aSC
,
433 const nsTArray
<SVGMaskFrame
*>& aMaskFrames
,
434 const nsPoint
& aOffsetToUserSpace
) {
435 MOZ_ASSERT(aMaskFrames
.Length() > 0);
436 MOZ_ASSERT(aMaskDT
->GetFormat() == SurfaceFormat::A8
);
437 MOZ_ASSERT(aOpacity
== 1.0 || aMaskFrames
.Length() == 1);
439 const nsStyleSVGReset
* svgReset
= aSC
->StyleSVGReset();
440 gfxMatrix cssPxToDevPxMatrix
= SVGUtils::GetCSSPxToDevPxMatrix(aParams
.frame
);
442 nsPresContext
* presContext
= aParams
.frame
->PresContext();
443 gfxPoint devPixelOffsetToUserSpace
= nsLayoutUtils::PointToGfxPoint(
444 aOffsetToUserSpace
, presContext
->AppUnitsPerDevPixel());
446 gfxContext
maskContext(aMaskDT
, /* aPreserveTransform */ true);
448 bool isMaskComplete
= true;
450 // Multiple SVG masks interleave with image mask. Paint each layer onto
451 // aMaskDT one at a time.
452 for (int i
= aMaskFrames
.Length() - 1; i
>= 0; i
--) {
453 SVGMaskFrame
* maskFrame
= aMaskFrames
[i
];
454 CompositionOp compositionOp
=
455 (i
== int(aMaskFrames
.Length() - 1))
456 ? CompositionOp::OP_OVER
457 : nsCSSRendering::GetGFXCompositeMode(
458 svgReset
->mMask
.mLayers
[i
].mComposite
);
460 // maskFrame != nullptr means we get a SVG mask.
461 // maskFrame == nullptr means we get an image mask.
463 SVGMaskFrame::MaskParams
params(
464 maskContext
.GetDrawTarget(), aParams
.frame
, cssPxToDevPxMatrix
,
465 aOpacity
, svgReset
->mMask
.mLayers
[i
].mMaskMode
, aParams
.imgParams
);
466 RefPtr
<SourceSurface
> svgMask
= maskFrame
->GetMaskForMaskedFrame(params
);
468 Matrix tmp
= aMaskDT
->GetTransform();
469 aMaskDT
->SetTransform(Matrix());
470 aMaskDT
->MaskSurface(ColorPattern(DeviceColor(0.0, 0.0, 0.0, 1.0)),
471 svgMask
, Point(0, 0),
472 DrawOptions(1.0, compositionOp
));
473 aMaskDT
->SetTransform(tmp
);
475 } else if (svgReset
->mMask
.mLayers
[i
].mImage
.IsResolved()) {
476 gfxContextMatrixAutoSaveRestore
matRestore(&maskContext
);
478 maskContext
.Multiply(gfxMatrix::Translation(-devPixelOffsetToUserSpace
));
479 nsCSSRendering::PaintBGParams params
=
480 nsCSSRendering::PaintBGParams::ForSingleLayer(
481 *presContext
, aParams
.dirtyRect
, aParams
.borderArea
,
483 aParams
.builder
->GetBackgroundPaintFlags() |
484 nsCSSRendering::PAINTBG_MASK_IMAGE
,
485 i
, compositionOp
, aOpacity
);
487 aParams
.imgParams
.result
&= nsCSSRendering::PaintStyleImageLayerWithSC(
488 params
, maskContext
, aSC
, *aParams
.frame
->StyleBorder());
490 isMaskComplete
= false;
494 return isMaskComplete
;
497 struct MaskPaintResult
{
498 RefPtr
<SourceSurface
> maskSurface
;
499 Matrix maskTransform
;
500 bool transparentBlackMask
;
503 MaskPaintResult() : transparentBlackMask(false), opacityApplied(false) {}
506 static MaskPaintResult
CreateAndPaintMaskSurface(
507 const PaintFramesParams
& aParams
, float aOpacity
, const ComputedStyle
* aSC
,
508 const nsTArray
<SVGMaskFrame
*>& aMaskFrames
,
509 const nsPoint
& aOffsetToUserSpace
) {
510 const nsStyleSVGReset
* svgReset
= aSC
->StyleSVGReset();
511 MOZ_ASSERT(aMaskFrames
.Length() > 0);
512 MaskPaintResult paintResult
;
514 gfxContext
& ctx
= aParams
.ctx
;
516 // Optimization for single SVG mask.
517 if (((aMaskFrames
.Length() == 1) && aMaskFrames
[0])) {
518 gfxMatrix cssPxToDevPxMatrix
=
519 SVGUtils::GetCSSPxToDevPxMatrix(aParams
.frame
);
520 paintResult
.opacityApplied
= true;
521 SVGMaskFrame::MaskParams
params(
522 ctx
.GetDrawTarget(), aParams
.frame
, cssPxToDevPxMatrix
, aOpacity
,
523 svgReset
->mMask
.mLayers
[0].mMaskMode
, aParams
.imgParams
);
524 paintResult
.maskSurface
= aMaskFrames
[0]->GetMaskForMaskedFrame(params
);
525 paintResult
.maskTransform
= ctx
.CurrentMatrix();
526 paintResult
.maskTransform
.Invert();
527 if (!paintResult
.maskSurface
) {
528 paintResult
.transparentBlackMask
= true;
534 const LayoutDeviceRect
& maskSurfaceRect
=
535 aParams
.maskRect
.valueOr(LayoutDeviceRect());
536 if (aParams
.maskRect
.isSome() && maskSurfaceRect
.IsEmpty()) {
537 // XXX: Is this ever true?
538 paintResult
.transparentBlackMask
= true;
542 RefPtr
<DrawTarget
> maskDT
= ctx
.GetDrawTarget()->CreateClippedDrawTarget(
543 maskSurfaceRect
.ToUnknownRect(), SurfaceFormat::A8
);
544 if (!maskDT
|| !maskDT
->IsValid()) {
548 // We can paint mask along with opacity only if
549 // 1. There is only one mask, or
550 // 2. No overlap among masks.
551 // Collision detect in #2 is not that trivial, we only accept #1 here.
552 paintResult
.opacityApplied
= (aMaskFrames
.Length() == 1);
554 // Set context's matrix on maskContext, offset by the maskSurfaceRect's
555 // position. This makes sure that we combine the masks in device space.
556 Matrix maskSurfaceMatrix
= ctx
.CurrentMatrix();
558 bool isMaskComplete
= PaintMaskSurface(
559 aParams
, maskDT
, paintResult
.opacityApplied
? aOpacity
: 1.0, aSC
,
560 aMaskFrames
, aOffsetToUserSpace
);
562 if (!isMaskComplete
||
563 (aParams
.imgParams
.result
!= ImgDrawResult::SUCCESS
&&
564 aParams
.imgParams
.result
!= ImgDrawResult::SUCCESS_NOT_COMPLETE
&&
565 aParams
.imgParams
.result
!= ImgDrawResult::WRONG_SIZE
)) {
566 // Now we know the status of mask resource since we used it while painting.
567 // According to the return value of PaintMaskSurface, we know whether mask
568 // resource is resolvable or not.
571 // According to css-masking spec, always create a mask surface when
572 // we have any item in maskFrame even if all of those items are
573 // non-resolvable <mask-sources> or <images>.
574 // Set paintResult.transparentBlackMask as true, the caller should stop
575 // painting masked content as if this mask is a transparent black one.
577 // SVG 1.1 say that if we fail to resolve a mask, we should draw the
579 // Left paintResult.maskSurface empty, the caller should paint all
580 // masked content as if this mask is an opaque white one(no mask).
581 paintResult
.transparentBlackMask
=
582 !aParams
.frame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
);
584 MOZ_ASSERT(!paintResult
.maskSurface
);
588 paintResult
.maskTransform
= maskSurfaceMatrix
;
589 if (!paintResult
.maskTransform
.Invert()) {
593 paintResult
.maskSurface
= maskDT
->Snapshot();
597 static bool ValidateSVGFrame(nsIFrame
* aFrame
) {
599 !aFrame
->HasAllStateBits(NS_FRAME_SVG_LAYOUT
| NS_FRAME_IS_NONDISPLAY
),
600 "Should not use SVGIntegrationUtils on this SVG frame");
602 if (aFrame
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
)) {
604 ISVGDisplayableFrame
* svgFrame
= do_QueryFrame(aFrame
);
605 MOZ_ASSERT(svgFrame
&& aFrame
->GetContent()->IsSVGElement(),
606 "A non-SVG frame carries NS_FRAME_SVG_LAYOUT flag?");
609 const nsIContent
* content
= aFrame
->GetContent();
610 if (!static_cast<const SVGElement
*>(content
)->HasValidDimensions()) {
611 // The SVG spec says not to draw _anything_
619 bool SVGIntegrationUtils::PaintMask(const PaintFramesParams
& aParams
,
620 bool& aOutIsMaskComplete
) {
621 aOutIsMaskComplete
= true;
623 SVGUtils::MaskUsage maskUsage
=
624 SVGUtils::DetermineMaskUsage(aParams
.frame
, aParams
.handleOpacity
);
625 if (!maskUsage
.ShouldDoSomething()) {
629 nsIFrame
* frame
= aParams
.frame
;
630 if (!ValidateSVGFrame(frame
)) {
634 gfxContext
& ctx
= aParams
.ctx
;
635 RefPtr
<DrawTarget
> maskTarget
= ctx
.GetDrawTarget();
637 if (maskUsage
.ShouldGenerateMaskLayer() && maskUsage
.HasSVGClip()) {
638 // We will paint both mask of positioned mask and clip-path into
641 // Create one extra draw target for drawing positioned mask, so that we do
642 // not have to copy the content of maskTarget before painting
643 // clip-path into it.
644 maskTarget
= maskTarget
->CreateClippedDrawTarget(Rect(), SurfaceFormat::A8
);
647 nsIFrame
* firstFrame
=
648 nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame
);
649 nsTArray
<SVGMaskFrame
*> maskFrames
;
650 // XXX check return value?
651 SVGObserverUtils::GetAndObserveMasks(firstFrame
, &maskFrames
);
653 gfxGroupForBlendAutoSaveRestore
autoPop(&ctx
);
654 bool shouldPushOpacity
= !maskUsage
.IsOpaque() && maskFrames
.Length() != 1;
655 if (shouldPushOpacity
) {
656 autoPop
.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA
,
657 maskUsage
.Opacity());
660 gfxContextMatrixAutoSaveRestore matSR
;
662 // Paint clip-path-basic-shape onto ctx
663 gfxContextAutoSaveRestore basicShapeSR
;
664 if (maskUsage
.ShouldApplyBasicShapeOrPath()) {
665 matSR
.SetContext(&ctx
);
667 MoveContextOriginToUserSpace(firstFrame
, aParams
);
669 basicShapeSR
.SetContext(&ctx
);
670 gfxMatrix mat
= SVGUtils::GetCSSPxToDevPxMatrix(frame
);
671 if (!maskUsage
.ShouldGenerateMaskLayer()) {
672 // Only have basic-shape clip-path effect. Fill clipped region by
674 ctx
.SetDeviceColor(DeviceColor::MaskOpaqueWhite());
675 RefPtr
<Path
> path
= CSSClipPathInstance::CreateClipPathForFrame(
676 ctx
.GetDrawTarget(), frame
, mat
);
684 CSSClipPathInstance::ApplyBasicShapeOrPathClip(ctx
, frame
, mat
);
687 // Paint mask into maskTarget.
688 if (maskUsage
.ShouldGenerateMaskLayer()) {
690 matSR
.SetContext(&ctx
);
692 EffectOffsets offsets
= ComputeEffectOffset(frame
, aParams
);
693 maskTarget
->SetTransform(maskTarget
->GetTransform().PreTranslate(
694 ToPoint(offsets
.offsetToUserSpaceInDevPx
)));
695 aOutIsMaskComplete
= PaintMaskSurface(
696 aParams
, maskTarget
, shouldPushOpacity
? 1.0f
: maskUsage
.Opacity(),
697 firstFrame
->Style(), maskFrames
, offsets
.offsetToUserSpace
);
700 // Paint clip-path onto ctx.
701 if (maskUsage
.HasSVGClip()) {
703 matSR
.SetContext(&ctx
);
705 MoveContextOriginToUserSpace(firstFrame
, aParams
);
706 Matrix clipMaskTransform
;
707 gfxMatrix cssPxToDevPxMatrix
= SVGUtils::GetCSSPxToDevPxMatrix(frame
);
709 SVGClipPathFrame
* clipPathFrame
;
710 // XXX check return value?
711 SVGObserverUtils::GetAndObserveClipPath(firstFrame
, &clipPathFrame
);
712 RefPtr
<SourceSurface
> maskSurface
=
713 maskUsage
.ShouldGenerateMaskLayer() ? maskTarget
->Snapshot() : nullptr;
714 clipPathFrame
->PaintClipMask(ctx
, frame
, cssPxToDevPxMatrix
, maskSurface
);
721 void PaintMaskAndClipPathInternal(const PaintFramesParams
& aParams
,
722 const T
& aPaintChild
) {
724 const nsStyleSVGReset
* style
= aParams
.frame
->StyleSVGReset();
725 MOZ_ASSERT(style
->HasClipPath() || style
->HasMask(),
726 "Should not use this method when no mask or clipPath effect"
730 /* SVG defines the following rendering model:
734 * 3. Apply clipping, masking, group opacity
736 * We handle #3 here and perform a couple of optimizations:
738 * + Use cairo's clipPath when representable natively (single object
741 * + Merge opacity and masking if both used together.
743 nsIFrame
* frame
= aParams
.frame
;
744 if (!ValidateSVGFrame(frame
)) {
748 SVGUtils::MaskUsage maskUsage
=
749 SVGUtils::DetermineMaskUsage(aParams
.frame
, aParams
.handleOpacity
);
751 if (maskUsage
.IsTransparent()) {
755 gfxContext
& context
= aParams
.ctx
;
756 gfxContextMatrixAutoSaveRestore
matrixAutoSaveRestore(&context
);
758 nsIFrame
* firstFrame
=
759 nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame
);
761 SVGClipPathFrame
* clipPathFrame
;
762 // XXX check return value?
763 SVGObserverUtils::GetAndObserveClipPath(firstFrame
, &clipPathFrame
);
765 nsTArray
<SVGMaskFrame
*> maskFrames
;
766 // XXX check return value?
767 SVGObserverUtils::GetAndObserveMasks(firstFrame
, &maskFrames
);
769 gfxMatrix cssPxToDevPxMatrix
= SVGUtils::GetCSSPxToDevPxMatrix(frame
);
771 bool shouldPushMask
= false;
773 gfxGroupForBlendAutoSaveRestore
autoGroupForBlend(&context
);
775 /* Check if we need to do additional operations on this child's
776 * rendering, which necessitates rendering into another surface. */
777 if (maskUsage
.ShouldGenerateMask()) {
778 gfxContextMatrixAutoSaveRestore matSR
;
780 RefPtr
<SourceSurface
> maskSurface
;
781 bool opacityApplied
= false;
783 if (maskUsage
.ShouldGenerateMaskLayer()) {
784 matSR
.SetContext(&context
);
786 // For css-mask, we want to generate a mask for each continuation frame,
787 // so we setup context matrix by the position of the current frame,
788 // instead of the first continuation frame.
789 EffectOffsets offsets
= MoveContextOriginToUserSpace(frame
, aParams
);
790 MaskPaintResult paintResult
= CreateAndPaintMaskSurface(
791 aParams
, maskUsage
.Opacity(), firstFrame
->Style(), maskFrames
,
792 offsets
.offsetToUserSpace
);
794 if (paintResult
.transparentBlackMask
) {
798 maskSurface
= paintResult
.maskSurface
;
800 shouldPushMask
= true;
802 opacityApplied
= paintResult
.opacityApplied
;
806 if (maskUsage
.ShouldGenerateClipMaskLayer()) {
808 matSR
.SetContext(&context
);
810 MoveContextOriginToUserSpace(firstFrame
, aParams
);
811 RefPtr
<SourceSurface
> clipMaskSurface
= clipPathFrame
->GetClipMask(
812 context
, frame
, cssPxToDevPxMatrix
, maskSurface
);
814 if (clipMaskSurface
) {
815 maskSurface
= clipMaskSurface
;
817 // Either entire surface is clipped out, or gfx buffer allocation
818 // failure in SVGClipPathFrame::GetClipMask.
822 shouldPushMask
= true;
826 if (!maskUsage
.ShouldGenerateLayer()) {
827 MOZ_ASSERT(!maskUsage
.IsOpaque());
829 matSR
.SetContext(&context
);
830 MoveContextOriginToUserSpace(firstFrame
, aParams
);
831 shouldPushMask
= true;
834 if (shouldPushMask
) {
835 // We want the mask to be untransformed so use the inverse of the
836 // current transform as the maskTransform to compensate.
837 Matrix maskTransform
= context
.CurrentMatrix();
838 maskTransform
.Invert();
840 autoGroupForBlend
.PushGroupForBlendBack(
841 gfxContentType::COLOR_ALPHA
,
842 opacityApplied
? 1.0f
: maskUsage
.Opacity(), maskSurface
,
847 /* If this frame has only a trivial clipPath, set up cairo's clipping now so
848 * we can just do normal painting and get it clipped appropriately.
850 if (maskUsage
.ShouldApplyClipPath() ||
851 maskUsage
.ShouldApplyBasicShapeOrPath()) {
852 gfxContextMatrixAutoSaveRestore
matSR(&context
);
854 MoveContextOriginToUserSpace(firstFrame
, aParams
);
856 MOZ_ASSERT(!maskUsage
.ShouldApplyClipPath() ||
857 !maskUsage
.ShouldApplyBasicShapeOrPath());
858 if (maskUsage
.ShouldApplyClipPath()) {
859 clipPathFrame
->ApplyClipPath(context
, frame
, cssPxToDevPxMatrix
);
861 CSSClipPathInstance::ApplyBasicShapeOrPathClip(context
, frame
,
866 /* Paint the child */
867 context
.SetMatrix(matrixAutoSaveRestore
.Matrix());
870 if (StaticPrefs::layers_draw_mask_debug()) {
871 gfxContextAutoSaveRestore
saver(&context
);
874 gfxRect drawingRect
= nsLayoutUtils::RectToGfxRect(
875 aParams
.borderArea
, frame
->PresContext()->AppUnitsPerDevPixel());
876 context
.SnappedRectangle(drawingRect
);
877 sRGBColor
overlayColor(0.0f
, 0.0f
, 0.0f
, 0.8f
);
878 if (maskUsage
.ShouldGenerateMaskLayer()) {
879 overlayColor
.r
= 1.0f
; // red represents css positioned mask.
881 if (maskUsage
.HasSVGClip()) {
882 overlayColor
.g
= 1.0f
; // green represents clip-path:<clip-source>.
884 if (maskUsage
.ShouldApplyBasicShapeOrPath()) {
885 overlayColor
.b
= 1.0f
; // blue represents
886 // clip-path:<basic-shape>||<geometry-box>.
889 context
.SetColor(overlayColor
);
893 if (maskUsage
.ShouldApplyClipPath() ||
894 maskUsage
.ShouldApplyBasicShapeOrPath()) {
899 void SVGIntegrationUtils::PaintMaskAndClipPath(
900 const PaintFramesParams
& aParams
,
901 const std::function
<void()>& aPaintChild
) {
902 PaintMaskAndClipPathInternal(aParams
, aPaintChild
);
905 void SVGIntegrationUtils::PaintFilter(const PaintFramesParams
& aParams
,
906 Span
<const StyleFilter
> aFilters
,
907 const SVGFilterPaintCallback
& aCallback
) {
908 MOZ_ASSERT(!aParams
.builder
->IsForGenerateGlyphMask(),
909 "Filter effect is discarded while generating glyph mask.");
910 MOZ_ASSERT(!aFilters
.IsEmpty(),
911 "Should not use this method when no filter effect on this frame");
913 nsIFrame
* frame
= aParams
.frame
;
914 if (!ValidateSVGFrame(frame
)) {
918 float opacity
= SVGUtils::ComputeOpacity(frame
, aParams
.handleOpacity
);
919 if (opacity
== 0.0f
) {
923 // Properties are added lazily and may have been removed by a restyle, so make
924 // sure all applicable ones are set again.
925 nsIFrame
* firstFrame
=
926 nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame
);
927 // Note: we do not return here for eHasNoRefs since we must still handle any
928 // CSS filter functions.
929 // XXX: Do we need to check for eHasRefsSomeInvalid here given that
930 // nsDisplayFilter::BuildLayer returns nullptr for eHasRefsSomeInvalid?
931 // Or can we just assert !eHasRefsSomeInvalid?
932 nsTArray
<SVGFilterFrame
*> filterFrames
;
933 if (SVGObserverUtils::GetAndObserveFilters(firstFrame
, &filterFrames
) ==
934 SVGObserverUtils::eHasRefsSomeInvalid
) {
935 aCallback(aParams
.ctx
, aParams
.imgParams
, nullptr, nullptr);
939 gfxContext
& context
= aParams
.ctx
;
941 gfxContextAutoSaveRestore
autoSR(&context
);
942 EffectOffsets offsets
= MoveContextOriginToUserSpace(firstFrame
, aParams
);
944 /* Paint the child and apply filters */
945 nsRegion dirtyRegion
= aParams
.dirtyRect
- offsets
.offsetToBoundingBox
;
947 FilterInstance::PaintFilteredFrame(frame
, aFilters
, filterFrames
, &context
,
948 aCallback
, &dirtyRegion
, aParams
.imgParams
,
952 bool SVGIntegrationUtils::CreateWebRenderCSSFilters(
953 Span
<const StyleFilter
> aFilters
, nsIFrame
* aFrame
,
954 WrFiltersHolder
& aWrFilters
) {
955 // All CSS filters are supported by WebRender. SVG filters are not fully
956 // supported, those use NS_STYLE_FILTER_URL and are handled separately.
958 // If there are too many filters to render, then just pretend that we
959 // succeeded, and don't render any of them.
960 if (aFilters
.Length() >
961 StaticPrefs::gfx_webrender_max_filter_ops_per_chain()) {
964 aWrFilters
.filters
.SetCapacity(aFilters
.Length());
965 auto& wrFilters
= aWrFilters
.filters
;
966 for (const StyleFilter
& filter
: aFilters
) {
967 switch (filter
.tag
) {
968 case StyleFilter::Tag::Brightness
:
969 wrFilters
.AppendElement(
970 wr::FilterOp::Brightness(filter
.AsBrightness()));
972 case StyleFilter::Tag::Contrast
:
973 wrFilters
.AppendElement(wr::FilterOp::Contrast(filter
.AsContrast()));
975 case StyleFilter::Tag::Grayscale
:
976 wrFilters
.AppendElement(wr::FilterOp::Grayscale(filter
.AsGrayscale()));
978 case StyleFilter::Tag::Invert
:
979 wrFilters
.AppendElement(wr::FilterOp::Invert(filter
.AsInvert()));
981 case StyleFilter::Tag::Opacity
: {
982 float opacity
= filter
.AsOpacity();
983 wrFilters
.AppendElement(wr::FilterOp::Opacity(
984 wr::PropertyBinding
<float>::Value(opacity
), opacity
));
987 case StyleFilter::Tag::Saturate
:
988 wrFilters
.AppendElement(wr::FilterOp::Saturate(filter
.AsSaturate()));
990 case StyleFilter::Tag::Sepia
:
991 wrFilters
.AppendElement(wr::FilterOp::Sepia(filter
.AsSepia()));
993 case StyleFilter::Tag::HueRotate
: {
994 wrFilters
.AppendElement(
995 wr::FilterOp::HueRotate(filter
.AsHueRotate().ToDegrees()));
998 case StyleFilter::Tag::Blur
: {
999 // TODO(emilio): we should go directly from css pixels -> device pixels.
1000 float appUnitsPerDevPixel
=
1001 aFrame
->PresContext()->AppUnitsPerDevPixel();
1002 float radius
= NSAppUnitsToFloatPixels(filter
.AsBlur().ToAppUnits(),
1003 appUnitsPerDevPixel
);
1004 wrFilters
.AppendElement(wr::FilterOp::Blur(radius
, radius
));
1007 case StyleFilter::Tag::DropShadow
: {
1008 float appUnitsPerDevPixel
=
1009 aFrame
->PresContext()->AppUnitsPerDevPixel();
1010 const StyleSimpleShadow
& shadow
= filter
.AsDropShadow();
1011 nscolor color
= shadow
.color
.CalcColor(aFrame
);
1013 wr::Shadow wrShadow
;
1015 NSAppUnitsToFloatPixels(shadow
.horizontal
.ToAppUnits(),
1016 appUnitsPerDevPixel
),
1017 NSAppUnitsToFloatPixels(shadow
.vertical
.ToAppUnits(),
1018 appUnitsPerDevPixel
)};
1019 wrShadow
.blur_radius
= NSAppUnitsToFloatPixels(shadow
.blur
.ToAppUnits(),
1020 appUnitsPerDevPixel
);
1021 wrShadow
.color
= {NS_GET_R(color
) / 255.0f
, NS_GET_G(color
) / 255.0f
,
1022 NS_GET_B(color
) / 255.0f
, NS_GET_A(color
) / 255.0f
};
1023 wrFilters
.AppendElement(wr::FilterOp::DropShadow(wrShadow
));
1034 bool SVGIntegrationUtils::BuildWebRenderFilters(
1035 nsIFrame
* aFilteredFrame
, Span
<const StyleFilter
> aFilters
,
1036 StyleFilterType aStyleFilterType
, WrFiltersHolder
& aWrFilters
,
1037 bool& aInitialized
) {
1038 return FilterInstance::BuildWebRenderFilters(
1039 aFilteredFrame
, aFilters
, aStyleFilterType
, aWrFilters
, aInitialized
);
1042 bool SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(nsIFrame
* aFrame
) {
1043 WrFiltersHolder wrFilters
;
1044 auto filterChain
= aFrame
->StyleEffects()->mFilters
.AsSpan();
1045 bool initialized
= true;
1046 return CreateWebRenderCSSFilters(filterChain
, aFrame
, wrFilters
) ||
1047 BuildWebRenderFilters(aFrame
, filterChain
, StyleFilterType::Filter
,
1048 wrFilters
, initialized
);
1051 bool SVGIntegrationUtils::UsesSVGEffectsNotSupportedInCompositor(
1053 // WebRender supports masks / clip-paths and some filters in the compositor.
1054 if (aFrame
->StyleEffects()->HasFilters()) {
1055 return !SVGIntegrationUtils::CanCreateWebRenderFiltersForFrame(aFrame
);
1060 class PaintFrameCallback
: public gfxDrawingCallback
{
1062 PaintFrameCallback(nsIFrame
* aFrame
, const nsSize aPaintServerSize
,
1063 const IntSize aRenderSize
, uint32_t aFlags
)
1065 mPaintServerSize(aPaintServerSize
),
1066 mRenderSize(aRenderSize
),
1068 virtual bool operator()(gfxContext
* aContext
, const gfxRect
& aFillRect
,
1069 const SamplingFilter aSamplingFilter
,
1070 const gfxMatrix
& aTransform
) override
;
1074 nsSize mPaintServerSize
;
1075 IntSize mRenderSize
;
1079 bool PaintFrameCallback::operator()(gfxContext
* aContext
,
1080 const gfxRect
& aFillRect
,
1081 const SamplingFilter aSamplingFilter
,
1082 const gfxMatrix
& aTransform
) {
1083 if (mFrame
->HasAnyStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER
)) {
1087 AutoSetRestorePaintServerState
paintServer(mFrame
);
1091 // Clip to aFillRect so that we don't paint outside.
1092 aContext
->Clip(aFillRect
);
1094 gfxMatrix invmatrix
= aTransform
;
1095 if (!invmatrix
.Invert()) {
1098 aContext
->Multiply(invmatrix
);
1100 // nsLayoutUtils::PaintFrame will anchor its painting at mFrame. But we want
1101 // to have it anchored at the top left corner of the bounding box of all of
1102 // mFrame's continuations. So we add a translation transform.
1103 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
1104 nsPoint offset
= SVGIntegrationUtils::GetOffsetToBoundingBox(mFrame
);
1105 gfxPoint devPxOffset
= gfxPoint(offset
.x
, offset
.y
) / appUnitsPerDevPixel
;
1106 aContext
->Multiply(gfxMatrix::Translation(devPxOffset
));
1108 gfxSize paintServerSize
=
1109 gfxSize(mPaintServerSize
.width
, mPaintServerSize
.height
) /
1110 mFrame
->PresContext()->AppUnitsPerDevPixel();
1112 // nsLayoutUtils::PaintFrame wants to render with paintServerSize, but we
1113 // want it to render with mRenderSize, so we need to set up a scale transform.
1114 gfxFloat scaleX
= mRenderSize
.width
/ paintServerSize
.width
;
1115 gfxFloat scaleY
= mRenderSize
.height
/ paintServerSize
.height
;
1116 aContext
->Multiply(gfxMatrix::Scaling(scaleX
, scaleY
));
1119 nsRect
dirty(-offset
.x
, -offset
.y
, mPaintServerSize
.width
,
1120 mPaintServerSize
.height
);
1122 using PaintFrameFlags
= nsLayoutUtils::PaintFrameFlags
;
1123 PaintFrameFlags flags
= PaintFrameFlags::InTransform
;
1124 if (mFlags
& SVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES
) {
1125 flags
|= PaintFrameFlags::SyncDecodeImages
;
1127 nsLayoutUtils::PaintFrame(aContext
, mFrame
, dirty
, NS_RGBA(0, 0, 0, 0),
1128 nsDisplayListBuilderMode::Painting
, flags
);
1130 nsIFrame
* currentFrame
= mFrame
;
1131 while ((currentFrame
= currentFrame
->GetNextContinuation()) != nullptr) {
1132 offset
= currentFrame
->GetOffsetToCrossDoc(mFrame
);
1133 devPxOffset
= gfxPoint(offset
.x
, offset
.y
) / appUnitsPerDevPixel
;
1136 aContext
->Multiply(gfxMatrix::Scaling(1 / scaleX
, 1 / scaleY
));
1137 aContext
->Multiply(gfxMatrix::Translation(devPxOffset
));
1138 aContext
->Multiply(gfxMatrix::Scaling(scaleX
, scaleY
));
1140 nsLayoutUtils::PaintFrame(aContext
, currentFrame
, dirty
- offset
,
1141 NS_RGBA(0, 0, 0, 0),
1142 nsDisplayListBuilderMode::Painting
, flags
);
1144 aContext
->Restore();
1147 aContext
->Restore();
1153 already_AddRefed
<gfxDrawable
> SVGIntegrationUtils::DrawableFromPaintServer(
1154 nsIFrame
* aFrame
, nsIFrame
* aTarget
, const nsSize
& aPaintServerSize
,
1155 const IntSize
& aRenderSize
, const DrawTarget
* aDrawTarget
,
1156 const gfxMatrix
& aContextMatrix
, uint32_t aFlags
) {
1157 // aPaintServerSize is the size that would be filled when using
1158 // background-repeat:no-repeat and background-size:auto. For normal background
1159 // images, this would be the intrinsic size of the image; for gradients and
1160 // patterns this would be the whole target frame fill area.
1161 // aRenderSize is what we will be actually filling after accounting for
1163 if (SVGPaintServerFrame
* server
= do_QueryFrame(aFrame
)) {
1164 // aFrame is either a pattern or a gradient. These fill the whole target
1165 // frame by default, so aPaintServerSize is the whole target background fill
1167 gfxRect
overrideBounds(0, 0, aPaintServerSize
.width
,
1168 aPaintServerSize
.height
);
1169 overrideBounds
.Scale(1.0 / aFrame
->PresContext()->AppUnitsPerDevPixel());
1170 uint32_t imgFlags
= imgIContainer::FLAG_ASYNC_NOTIFY
;
1171 if (aFlags
& SVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES
) {
1172 imgFlags
|= imgIContainer::FLAG_SYNC_DECODE
;
1174 imgDrawingParams
imgParams(imgFlags
);
1175 RefPtr
<gfxPattern
> pattern
= server
->GetPaintServerPattern(
1176 aTarget
, aDrawTarget
, aContextMatrix
, &nsStyleSVG::mFill
, 1.0,
1177 imgParams
, &overrideBounds
);
1183 // pattern is now set up to fill aPaintServerSize. But we want it to
1184 // fill aRenderSize, so we need to add a scaling transform.
1185 // We couldn't just have set overrideBounds to aRenderSize - it would have
1186 // worked for gradients, but for patterns it would result in a different
1188 gfxFloat scaleX
= overrideBounds
.Width() / aRenderSize
.width
;
1189 gfxFloat scaleY
= overrideBounds
.Height() / aRenderSize
.height
;
1190 gfxMatrix scaleMatrix
= gfxMatrix::Scaling(scaleX
, scaleY
);
1191 pattern
->SetMatrix(scaleMatrix
* pattern
->GetMatrix());
1192 RefPtr
<gfxDrawable
> drawable
= new gfxPatternDrawable(pattern
, aRenderSize
);
1193 return drawable
.forget();
1196 if (aFrame
->IsSVGFrame() &&
1197 !static_cast<ISVGDisplayableFrame
*>(do_QueryFrame(aFrame
))) {
1198 MOZ_ASSERT_UNREACHABLE(
1199 "We should prevent painting of unpaintable SVG "
1200 "before we get here");
1204 // We don't want to paint into a surface as long as we don't need to, so we
1205 // set up a drawing callback.
1206 RefPtr
<gfxDrawingCallback
> cb
=
1207 new PaintFrameCallback(aFrame
, aPaintServerSize
, aRenderSize
, aFlags
);
1208 RefPtr
<gfxDrawable
> drawable
= new gfxCallbackDrawable(cb
, aRenderSize
);
1209 return drawable
.forget();
1212 } // namespace mozilla