Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : // Main header first:
7 : #include "nsSVGIntegrationUtils.h"
8 :
9 : // Keep others in (case-insensitive) order:
10 : #include "gfxDrawable.h"
11 : #include "gfxPrefs.h"
12 : #include "nsCSSAnonBoxes.h"
13 : #include "nsCSSClipPathInstance.h"
14 : #include "nsDisplayList.h"
15 : #include "nsFilterInstance.h"
16 : #include "nsLayoutUtils.h"
17 : #include "gfxContext.h"
18 : #include "nsSVGClipPathFrame.h"
19 : #include "nsSVGEffects.h"
20 : #include "nsSVGElement.h"
21 : #include "nsSVGFilterPaintCallback.h"
22 : #include "nsSVGMaskFrame.h"
23 : #include "nsSVGPaintServerFrame.h"
24 : #include "nsSVGUtils.h"
25 : #include "FrameLayerBuilder.h"
26 : #include "BasicLayers.h"
27 : #include "mozilla/gfx/Point.h"
28 : #include "nsCSSRendering.h"
29 : #include "mozilla/Unused.h"
30 : #include "mozilla/GeckoRestyleManager.h"
31 :
32 : using namespace mozilla;
33 : using namespace mozilla::layers;
34 : using namespace mozilla::gfx;
35 : using namespace mozilla::image;
36 :
37 : // ----------------------------------------------------------------------
38 :
39 : /**
40 : * This class is used to get the pre-effects visual overflow rect of a frame,
41 : * or, in the case of a frame with continuations, to collect the union of the
42 : * pre-effects visual 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.
45 : */
46 5 : class PreEffectsVisualOverflowCollector : public nsLayoutUtils::BoxCallback
47 : {
48 : public:
49 : /**
50 : * If the pre-effects visual overflow rect of the frame being examined
51 : * happens to be known, it can be passed in as aCurrentFrame and its
52 : * pre-effects visual overflow rect can be passed in as
53 : * aCurrentFrameOverflowArea. This is just an optimization to save a
54 : * frame property lookup - these arguments are optional.
55 : */
56 5 : PreEffectsVisualOverflowCollector(nsIFrame* aFirstContinuation,
57 : nsIFrame* aCurrentFrame,
58 : const nsRect& aCurrentFrameOverflowArea,
59 : bool aInReflow)
60 5 : : mFirstContinuation(aFirstContinuation)
61 : , mCurrentFrame(aCurrentFrame)
62 : , mCurrentFrameOverflowArea(aCurrentFrameOverflowArea)
63 5 : , mInReflow(aInReflow)
64 : {
65 5 : NS_ASSERTION(!mFirstContinuation->GetPrevContinuation(),
66 : "We want the first continuation here");
67 5 : }
68 :
69 5 : virtual void AddBox(nsIFrame* aFrame) override {
70 5 : nsRect overflow = (aFrame == mCurrentFrame)
71 : ? mCurrentFrameOverflowArea
72 10 : : GetPreEffectsVisualOverflowRect(aFrame, mInReflow);
73 5 : mResult.UnionRect(mResult, overflow + aFrame->GetOffsetTo(mFirstContinuation));
74 5 : }
75 :
76 5 : nsRect GetResult() const {
77 5 : return mResult;
78 : }
79 :
80 : private:
81 :
82 5 : static nsRect GetPreEffectsVisualOverflowRect(nsIFrame* aFrame,
83 : bool aInReflow) {
84 5 : nsRect* r = aFrame->GetProperty(nsIFrame::PreEffectsBBoxProperty());
85 5 : if (r) {
86 1 : return *r;
87 : }
88 :
89 : #ifdef DEBUG
90 : // Having PreTransformOverflowAreasProperty cached means
91 : // GetVisualOverflowRect() will return post-effect rect, which is not what
92 : // we want. This function intentional reports pre-effect rect. But it does
93 : // not matter if there is no SVG effect on this frame, since no effect
94 : // means post-effect rect matches pre-effect rect.
95 : //
96 : // This function may be called during reflow or painting. We should only
97 : // do this check in painting process since the PreEffectsBBoxProperty of
98 : // continuations are not set correctly while reflowing.
99 4 : if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame) && !aInReflow) {
100 : nsOverflowAreas* preTransformOverflows =
101 4 : aFrame->GetProperty(aFrame->PreTransformOverflowAreasProperty());
102 :
103 4 : MOZ_ASSERT(!preTransformOverflows,
104 : "GetVisualOverflowRect() won't return the pre-effects rect!");
105 : }
106 : #endif
107 4 : return aFrame->GetVisualOverflowRect();
108 : }
109 :
110 : nsIFrame* mFirstContinuation;
111 : nsIFrame* mCurrentFrame;
112 : const nsRect& mCurrentFrameOverflowArea;
113 : nsRect mResult;
114 : bool mInReflow;
115 : };
116 :
117 : /**
118 : * Gets the union of the pre-effects visual overflow rects of all of a frame's
119 : * continuations, in "user space".
120 : */
121 : static nsRect
122 5 : GetPreEffectsVisualOverflowUnion(nsIFrame* aFirstContinuation,
123 : nsIFrame* aCurrentFrame,
124 : const nsRect& aCurrentFramePreEffectsOverflow,
125 : const nsPoint& aFirstContinuationToUserSpace,
126 : bool aInReflow)
127 : {
128 5 : NS_ASSERTION(!aFirstContinuation->GetPrevContinuation(),
129 : "Need first continuation here");
130 : PreEffectsVisualOverflowCollector collector(aFirstContinuation,
131 : aCurrentFrame,
132 : aCurrentFramePreEffectsOverflow,
133 10 : aInReflow);
134 : // Compute union of all overflow areas relative to aFirstContinuation:
135 5 : nsLayoutUtils::GetAllInFlowBoxes(aFirstContinuation, &collector);
136 : // Return the result in user space:
137 10 : return collector.GetResult() + aFirstContinuationToUserSpace;
138 : }
139 :
140 :
141 : bool
142 2699 : nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame)
143 : {
144 : // Even when SVG display lists are disabled, returning true for SVG frames
145 : // does not adversely affect any of our callers. Therefore we don't bother
146 : // checking the SDL prefs here, since we don't know if we're being called for
147 : // painting or hit-testing anyway.
148 2699 : const nsStyleSVGReset *style = aFrame->StyleSVGReset();
149 5398 : return aFrame->StyleEffects()->HasFilters() ||
150 5396 : style->HasClipPath() || style->HasMask();
151 : }
152 :
153 : bool
154 618 : nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(const nsIFrame* aFrame)
155 : {
156 618 : const nsStyleSVGReset *style = aFrame->StyleSVGReset();
157 618 : return style->HasClipPath() || style->HasMask();
158 : }
159 :
160 : nsPoint
161 13 : nsSVGIntegrationUtils::GetOffsetToBoundingBox(nsIFrame* aFrame)
162 : {
163 13 : if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
164 : // Do NOT call GetAllInFlowRectsUnion for SVG - it will get the
165 : // covered region relative to the nsSVGOuterSVGFrame, which is absolutely
166 : // not what we want. SVG frames are always in user space, so they have
167 : // no offset adjustment to make.
168 2 : return nsPoint();
169 : }
170 :
171 : // The GetAllInFlowRectsUnion() call gets the union of the frame border-box
172 : // rects over all continuations, relative to the origin (top-left of the
173 : // border box) of its second argument (here, aFrame, the first continuation).
174 11 : return -nsLayoutUtils::GetAllInFlowRectsUnion(aFrame, aFrame).TopLeft();
175 : }
176 :
177 : /* static */ nsSize
178 0 : nsSVGIntegrationUtils::GetContinuationUnionSize(nsIFrame* aNonSVGFrame)
179 : {
180 0 : NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
181 : "SVG frames should not get here");
182 : nsIFrame* firstFrame =
183 0 : nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame);
184 0 : return nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame, firstFrame).Size();
185 : }
186 :
187 : /* static */ gfx::Size
188 0 : nsSVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(nsIFrame* aNonSVGFrame)
189 : {
190 0 : NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
191 : "SVG frames should not get here");
192 : nsIFrame* firstFrame =
193 0 : nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame);
194 0 : nsRect r = nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame, firstFrame);
195 0 : nsPresContext* presContext = firstFrame->PresContext();
196 0 : return gfx::Size(presContext->AppUnitsToFloatCSSPixels(r.width),
197 0 : presContext->AppUnitsToFloatCSSPixels(r.height));
198 : }
199 :
200 : gfxRect
201 5 : nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame)
202 : {
203 : // Except for nsSVGOuterSVGFrame, we shouldn't be getting here with SVG
204 : // frames at all. This function is for elements that are laid out using the
205 : // CSS box model rules.
206 5 : NS_ASSERTION(!(aNonSVGFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT),
207 : "Frames with SVG layout should not get here");
208 5 : MOZ_ASSERT(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG) ||
209 : aNonSVGFrame->IsSVGOuterSVGFrame());
210 :
211 : nsIFrame* firstFrame =
212 5 : nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame);
213 : // 'r' is in "user space":
214 10 : nsRect r = GetPreEffectsVisualOverflowUnion(firstFrame, nullptr, nsRect(),
215 10 : GetOffsetToBoundingBox(firstFrame),
216 10 : false);
217 : return nsLayoutUtils::RectToGfxRect(r,
218 10 : aNonSVGFrame->PresContext()->AppUnitsPerCSSPixel());
219 : }
220 :
221 : // XXX Since we're called during reflow, this method is broken for frames with
222 : // continuations. When we're called for a frame with continuations, we're
223 : // called for each continuation in turn as it's reflowed. However, it isn't
224 : // until the last continuation is reflowed that this method's
225 : // GetOffsetToBoundingBox() and GetPreEffectsVisualOverflowUnion() calls will
226 : // obtain valid border boxes for all the continuations. As a result, we'll
227 : // end up returning bogus post-filter visual overflow rects for all the prior
228 : // continuations. Unfortunately, by the time the last continuation is
229 : // reflowed, it's too late to go back and set and propagate the overflow
230 : // rects on the previous continuations.
231 : //
232 : // The reason that we need to pass an override bbox to
233 : // GetPreEffectsVisualOverflowUnion rather than just letting it call into our
234 : // GetSVGBBoxForNonSVGFrame method is because we get called by
235 : // ComputeEffectsRect when it has been called with
236 : // aStoreRectProperties set to false. In this case the pre-effects visual
237 : // overflow rect that it has been passed may be different to that stored on
238 : // aFrame, resulting in a different bbox.
239 : //
240 : // XXXjwatt The pre-effects visual overflow rect passed to
241 : // ComputeEffectsRect won't include continuation overflows, so
242 : // for frames with continuation the following filter analysis will likely end
243 : // up being carried out with a bbox created as if the frame didn't have
244 : // continuations.
245 : //
246 : // XXXjwatt Using aPreEffectsOverflowRect to create the bbox isn't really right
247 : // for SVG frames, since for SVG frames the SVG spec defines the bbox to be
248 : // something quite different to the pre-effects visual overflow rect. However,
249 : // we're essentially calculating an invalidation area here, and using the
250 : // pre-effects overflow rect will actually overestimate that area which, while
251 : // being a bit wasteful, isn't otherwise a problem.
252 : //
253 : nsRect
254 1 : nsSVGIntegrationUtils::
255 : ComputePostEffectsVisualOverflowRect(nsIFrame* aFrame,
256 : const nsRect& aPreEffectsOverflowRect)
257 : {
258 1 : NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT),
259 : "Don't call this on SVG child frames");
260 :
261 : nsIFrame* firstFrame =
262 1 : nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
263 : nsSVGEffects::EffectProperties effectProperties =
264 1 : nsSVGEffects::GetEffectProperties(firstFrame);
265 1 : if (!effectProperties.HasValidFilter()) {
266 1 : return aPreEffectsOverflowRect;
267 : }
268 :
269 : // Create an override bbox - see comment above:
270 0 : nsPoint firstFrameToBoundingBox = GetOffsetToBoundingBox(firstFrame);
271 : // overrideBBox is in "user space", in _CSS_ pixels:
272 : // XXX Why are we rounding out to pixel boundaries? We don't do that in
273 : // GetSVGBBoxForNonSVGFrame, and it doesn't appear to be necessary.
274 : gfxRect overrideBBox =
275 : nsLayoutUtils::RectToGfxRect(
276 0 : GetPreEffectsVisualOverflowUnion(firstFrame, aFrame,
277 : aPreEffectsOverflowRect,
278 : firstFrameToBoundingBox,
279 : true),
280 0 : aFrame->PresContext()->AppUnitsPerCSSPixel());
281 0 : overrideBBox.RoundOut();
282 :
283 : nsRect overflowRect =
284 0 : nsFilterInstance::GetPostFilterBounds(firstFrame, &overrideBBox);
285 :
286 : // Return overflowRect relative to aFrame, rather than "user space":
287 0 : return overflowRect - (aFrame->GetOffsetTo(firstFrame) + firstFrameToBoundingBox);
288 : }
289 :
290 : nsIntRegion
291 7 : nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame,
292 : const nsPoint& aToReferenceFrame,
293 : const nsIntRegion& aInvalidRegion)
294 : {
295 7 : if (aInvalidRegion.IsEmpty()) {
296 3 : return nsIntRect();
297 : }
298 :
299 : // Don't bother calling GetEffectProperties; the filter property should
300 : // already have been set up during reflow/ComputeFrameEffectsRect
301 : nsIFrame* firstFrame =
302 4 : nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
303 4 : nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame);
304 4 : if (!prop || !prop->IsInObserverLists()) {
305 4 : return aInvalidRegion;
306 : }
307 :
308 0 : int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
309 :
310 0 : if (!prop || !prop->ReferencesValidResources()) {
311 : // The frame is either not there or not currently available,
312 : // perhaps because we're in the middle of tearing stuff down.
313 : // Be conservative, return our visual overflow rect relative
314 : // to the reference frame.
315 0 : nsRect overflow = aFrame->GetVisualOverflowRect() + aToReferenceFrame;
316 0 : return overflow.ToOutsidePixels(appUnitsPerDevPixel);
317 : }
318 :
319 : // Convert aInvalidRegion into bounding box frame space in app units:
320 : nsPoint toBoundingBox =
321 0 : aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame);
322 : // The initial rect was relative to the reference frame, so we need to
323 : // remove that offset to get a rect relative to the current frame.
324 0 : toBoundingBox -= aToReferenceFrame;
325 0 : nsRegion preEffectsRegion = aInvalidRegion.ToAppUnits(appUnitsPerDevPixel).MovedBy(toBoundingBox);
326 :
327 : // Adjust the dirty area for effects, and shift it back to being relative to
328 : // the reference frame.
329 0 : nsRegion result = nsFilterInstance::GetPostFilterDirtyArea(firstFrame,
330 0 : preEffectsRegion).MovedBy(-toBoundingBox);
331 : // Return the result, in pixels relative to the reference frame.
332 0 : return result.ToOutsidePixels(appUnitsPerDevPixel);
333 : }
334 :
335 : nsRect
336 7 : nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
337 : const nsRect& aDirtyRect)
338 : {
339 : // Don't bother calling GetEffectProperties; the filter property should
340 : // already have been set up during reflow/ComputeFrameEffectsRect
341 : nsIFrame* firstFrame =
342 7 : nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
343 7 : nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame);
344 7 : if (!prop || !prop->ReferencesValidResources()) {
345 7 : return aDirtyRect;
346 : }
347 :
348 : // Convert aDirtyRect into "user space" in app units:
349 : nsPoint toUserSpace =
350 0 : aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame);
351 0 : nsRect postEffectsRect = aDirtyRect + toUserSpace;
352 :
353 : // Return ther result, relative to aFrame, not in user space:
354 0 : return nsFilterInstance::GetPreFilterNeededArea(firstFrame, postEffectsRect).GetBounds()
355 0 : - toUserSpace;
356 : }
357 :
358 : bool
359 0 : nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt)
360 : {
361 : nsIFrame* firstFrame =
362 0 : nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
363 : // Convert aPt to user space:
364 0 : nsPoint toUserSpace;
365 0 : if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
366 : // XXXmstange Isn't this wrong for svg:use and innerSVG frames?
367 0 : toUserSpace = aFrame->GetPosition();
368 : } else {
369 0 : toUserSpace =
370 0 : aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame);
371 : }
372 0 : nsPoint pt = aPt + toUserSpace;
373 : gfxPoint userSpacePt =
374 0 : gfxPoint(pt.x, pt.y) / aFrame->PresContext()->AppUnitsPerCSSPixel();
375 0 : return nsSVGUtils::HitTestClip(firstFrame, userSpacePt);
376 : }
377 :
378 : class RegularFramePaintCallback : public nsSVGFilterPaintCallback
379 : {
380 : public:
381 0 : RegularFramePaintCallback(nsDisplayListBuilder* aBuilder,
382 : LayerManager* aManager,
383 : const gfxPoint& aUserSpaceToFrameSpaceOffset)
384 0 : : mBuilder(aBuilder), mLayerManager(aManager),
385 0 : mUserSpaceToFrameSpaceOffset(aUserSpaceToFrameSpaceOffset) {}
386 :
387 0 : virtual void Paint(gfxContext& aContext, nsIFrame *aTarget,
388 : const gfxMatrix& aTransform,
389 : const nsIntRect* aDirtyRect,
390 : imgDrawingParams& aImgParams) override
391 : {
392 0 : BasicLayerManager* basic = mLayerManager->AsBasicLayerManager();
393 0 : RefPtr<gfxContext> oldCtx = basic->GetTarget();
394 0 : basic->SetTarget(&aContext);
395 :
396 0 : gfxContextMatrixAutoSaveRestore autoSR(&aContext);
397 0 : aContext.SetMatrix(aContext.CurrentMatrix().PreTranslate(-mUserSpaceToFrameSpaceOffset));
398 :
399 0 : mLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, mBuilder);
400 0 : basic->SetTarget(oldCtx);
401 0 : }
402 :
403 : private:
404 : nsDisplayListBuilder* mBuilder;
405 : LayerManager* mLayerManager;
406 : gfxPoint mUserSpaceToFrameSpaceOffset;
407 : };
408 :
409 : typedef nsSVGIntegrationUtils::PaintFramesParams PaintFramesParams;
410 :
411 : /**
412 : * Paint css-positioned-mask onto a given target(aMaskDT).
413 : */
414 : static void
415 3 : PaintMaskSurface(const PaintFramesParams& aParams,
416 : DrawTarget* aMaskDT, float aOpacity, nsStyleContext* aSC,
417 : const nsTArray<nsSVGMaskFrame*>& aMaskFrames,
418 : const gfxMatrix& aMaskSurfaceMatrix,
419 : const nsPoint& aOffsetToUserSpace)
420 : {
421 3 : MOZ_ASSERT(aMaskFrames.Length() > 0);
422 3 : MOZ_ASSERT(aMaskDT->GetFormat() == SurfaceFormat::A8);
423 3 : MOZ_ASSERT(aOpacity == 1.0 || aMaskFrames.Length() == 1);
424 :
425 3 : const nsStyleSVGReset *svgReset = aSC->StyleSVGReset();
426 : gfxMatrix cssPxToDevPxMatrix =
427 3 : nsSVGUtils::GetCSSPxToDevPxMatrix(aParams.frame);
428 :
429 3 : nsPresContext* presContext = aParams.frame->PresContext();
430 : gfxPoint devPixelOffsetToUserSpace =
431 : nsLayoutUtils::PointToGfxPoint(aOffsetToUserSpace,
432 3 : presContext->AppUnitsPerDevPixel());
433 :
434 6 : RefPtr<gfxContext> maskContext = gfxContext::CreateOrNull(aMaskDT);
435 3 : MOZ_ASSERT(maskContext);
436 3 : maskContext->SetMatrix(aMaskSurfaceMatrix);
437 :
438 : // Multiple SVG masks interleave with image mask. Paint each layer onto
439 : // aMaskDT one at a time.
440 6 : for (int i = aMaskFrames.Length() - 1; i >= 0 ; i--) {
441 3 : nsSVGMaskFrame *maskFrame = aMaskFrames[i];
442 3 : CompositionOp compositionOp = (i == int(aMaskFrames.Length() - 1))
443 3 : ? CompositionOp::OP_OVER
444 3 : : nsCSSRendering::GetGFXCompositeMode(svgReset->mMask.mLayers[i].mComposite);
445 :
446 : // maskFrame != nullptr means we get a SVG mask.
447 : // maskFrame == nullptr means we get an image mask.
448 3 : if (maskFrame) {
449 0 : Matrix svgMaskMatrix;
450 0 : nsSVGMaskFrame::MaskParams params(maskContext, aParams.frame,
451 : cssPxToDevPxMatrix,
452 : aOpacity, &svgMaskMatrix,
453 0 : svgReset->mMask.mLayers[i].mMaskMode,
454 0 : aParams.imgParams);
455 0 : RefPtr<SourceSurface> svgMask = maskFrame->GetMaskForMaskedFrame(params);
456 0 : if (svgMask) {
457 0 : gfxContextMatrixAutoSaveRestore matRestore(maskContext);
458 :
459 0 : maskContext->Multiply(ThebesMatrix(svgMaskMatrix));
460 0 : aMaskDT->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)), svgMask,
461 : Point(0, 0),
462 0 : DrawOptions(1.0, compositionOp));
463 : }
464 : } else {
465 6 : gfxContextMatrixAutoSaveRestore matRestore(maskContext);
466 :
467 3 : maskContext->Multiply(gfxMatrix::Translation(-devPixelOffsetToUserSpace));
468 : nsCSSRendering::PaintBGParams params =
469 : nsCSSRendering::PaintBGParams::ForSingleLayer(*presContext,
470 : aParams.dirtyRect,
471 : aParams.borderArea,
472 3 : aParams.frame,
473 3 : aParams.builder->GetBackgroundPaintFlags() |
474 : nsCSSRendering::PAINTBG_MASK_IMAGE,
475 : i, compositionOp,
476 9 : aOpacity);
477 :
478 3 : aParams.imgParams.result &=
479 : nsCSSRendering::PaintStyleImageLayerWithSC(params, *maskContext, aSC,
480 6 : *aParams.frame->StyleBorder());
481 : }
482 : }
483 3 : }
484 :
485 3 : struct MaskPaintResult {
486 : RefPtr<SourceSurface> maskSurface;
487 : Matrix maskTransform;
488 : bool transparentBlackMask;
489 : bool opacityApplied;
490 :
491 3 : MaskPaintResult()
492 3 : : transparentBlackMask(false)
493 3 : , opacityApplied(false)
494 3 : {}
495 : };
496 :
497 : static MaskPaintResult
498 3 : CreateAndPaintMaskSurface(const PaintFramesParams& aParams,
499 : float aOpacity, nsStyleContext* aSC,
500 : const nsTArray<nsSVGMaskFrame*>& aMaskFrames,
501 : const nsPoint& aOffsetToUserSpace)
502 : {
503 3 : const nsStyleSVGReset *svgReset = aSC->StyleSVGReset();
504 3 : MOZ_ASSERT(aMaskFrames.Length() > 0);
505 3 : MaskPaintResult paintResult;
506 :
507 3 : gfxContext& ctx = aParams.ctx;
508 :
509 : // Optimization for single SVG mask.
510 3 : if (((aMaskFrames.Length() == 1) && aMaskFrames[0])) {
511 : gfxMatrix cssPxToDevPxMatrix =
512 0 : nsSVGUtils::GetCSSPxToDevPxMatrix(aParams.frame);
513 0 : paintResult.opacityApplied = true;
514 0 : nsSVGMaskFrame::MaskParams params(&ctx, aParams.frame, cssPxToDevPxMatrix,
515 : aOpacity, &paintResult.maskTransform,
516 0 : svgReset->mMask.mLayers[0].mMaskMode,
517 0 : aParams.imgParams);
518 : paintResult.maskSurface =
519 0 : aMaskFrames[0]->GetMaskForMaskedFrame(params);
520 :
521 0 : if (!paintResult.maskSurface) {
522 0 : paintResult.transparentBlackMask = true;
523 : }
524 :
525 0 : return paintResult;
526 : }
527 :
528 3 : const IntRect& maskSurfaceRect = aParams.maskRect;
529 3 : if (maskSurfaceRect.IsEmpty()) {
530 0 : paintResult.transparentBlackMask = true;
531 0 : return paintResult;
532 : }
533 :
534 : RefPtr<DrawTarget> maskDT =
535 9 : ctx.GetDrawTarget()->CreateSimilarDrawTarget(maskSurfaceRect.Size(),
536 9 : SurfaceFormat::A8);
537 3 : if (!maskDT || !maskDT->IsValid()) {
538 0 : return paintResult;
539 : }
540 :
541 : // We can paint mask along with opacity only if
542 : // 1. There is only one mask, or
543 : // 2. No overlap among masks.
544 : // Collision detect in #2 is not that trivial, we only accept #1 here.
545 3 : paintResult.opacityApplied = (aMaskFrames.Length() == 1);
546 :
547 : // Set context's matrix on maskContext, offset by the maskSurfaceRect's
548 : // position. This makes sure that we combine the masks in device space.
549 : gfxMatrix maskSurfaceMatrix =
550 3 : ctx.CurrentMatrix() * gfxMatrix::Translation(-aParams.maskRect.TopLeft());
551 :
552 3 : PaintMaskSurface(aParams, maskDT,
553 3 : paintResult.opacityApplied ? aOpacity : 1.0,
554 : aSC, aMaskFrames, maskSurfaceMatrix,
555 3 : aOffsetToUserSpace);
556 :
557 3 : if (aParams.imgParams.result != DrawResult::SUCCESS) {
558 : // Now we know the status of mask resource since we used it while painting.
559 : // According to the return value of PaintMaskSurface, we know whether mask
560 : // resource is resolvable or not.
561 : //
562 : // For a HTML doc:
563 : // According to css-masking spec, always create a mask surface when
564 : // we have any item in maskFrame even if all of those items are
565 : // non-resolvable <mask-sources> or <images>.
566 : // Set paintResult.transparentBlackMask as true, the caller should stop
567 : // painting masked content as if this mask is a transparent black one.
568 : // For a SVG doc:
569 : // SVG 1.1 say that if we fail to resolve a mask, we should draw the
570 : // object unmasked.
571 : // Left patinResult.maskSurface empty, the caller should paint all
572 : // masked content as if this mask is an opaque white one(no mask).
573 0 : paintResult.transparentBlackMask =
574 0 : !(aParams.frame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
575 :
576 0 : MOZ_ASSERT(!paintResult.maskSurface);
577 0 : return paintResult;
578 : }
579 :
580 3 : paintResult.maskTransform = ToMatrix(maskSurfaceMatrix);
581 3 : if (!paintResult.maskTransform.Invert()) {
582 0 : return paintResult;
583 : }
584 :
585 3 : paintResult.maskSurface = maskDT->Snapshot();
586 3 : return paintResult;
587 : }
588 :
589 : static bool
590 5 : ValidateSVGFrame(nsIFrame* aFrame)
591 : {
592 : #ifdef DEBUG
593 5 : NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
594 : (NS_SVGDisplayListPaintingEnabled() &&
595 : !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)),
596 : "Should not use nsSVGIntegrationUtils on this SVG frame");
597 : #endif
598 :
599 5 : bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
600 5 : if (hasSVGLayout) {
601 : #ifdef DEBUG
602 2 : nsSVGDisplayableFrame* svgFrame = do_QueryFrame(aFrame);
603 2 : MOZ_ASSERT(svgFrame && aFrame->GetContent()->IsSVGElement(),
604 : "A non-SVG frame carries NS_FRAME_SVG_LAYOUT flag?");
605 : #endif
606 :
607 2 : const nsIContent* content = aFrame->GetContent();
608 2 : if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
609 : // The SVG spec says not to draw _anything_
610 0 : return false;
611 : }
612 : }
613 :
614 5 : return true;
615 : }
616 :
617 5 : struct EffectOffsets {
618 : // The offset between the reference frame and the bounding box of the
619 : // target frame in app unit.
620 : nsPoint offsetToBoundingBox;
621 : // The offset between the reference frame and the bounding box of the
622 : // target frame in device unit.
623 : gfxPoint offsetToBoundingBoxInDevPx;
624 : // The offset between the reference frame and the bounding box of the
625 : // target frame in app unit.
626 : nsPoint offsetToUserSpace;
627 : // The offset between the reference frame and the bounding box of the
628 : // target frame in device unit.
629 : gfxPoint offsetToUserSpaceInDevPx;
630 : };
631 :
632 : EffectOffsets
633 5 : ComputeEffectOffset(nsIFrame* aFrame, const PaintFramesParams& aParams)
634 : {
635 5 : EffectOffsets result;
636 :
637 5 : result.offsetToBoundingBox =
638 10 : aParams.builder->ToReferenceFrame(aFrame) -
639 10 : nsSVGIntegrationUtils::GetOffsetToBoundingBox(aFrame);
640 5 : if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
641 : /* Snap the offset if the reference frame is not a SVG frame,
642 : * since other frames will be snapped to pixel when rendering. */
643 3 : result.offsetToBoundingBox =
644 6 : nsPoint(
645 : aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(result.offsetToBoundingBox.x),
646 : aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(result.offsetToBoundingBox.y));
647 : }
648 :
649 : // After applying only "aOffsetToBoundingBox", aParams.ctx would have its
650 : // origin at the top left corner of frame's bounding box (over all
651 : // continuations).
652 : // However, SVG painting needs the origin to be located at the origin of the
653 : // SVG frame's "user space", i.e. the space in which, for example, the
654 : // frame's BBox lives.
655 : // SVG geometry frames and foreignObject frames apply their own offsets, so
656 : // their position is relative to their user space. So for these frame types,
657 : // if we want aParams.ctx to be in user space, we first need to subtract the
658 : // frame's position so that SVG painting can later add it again and the
659 : // frame is painted in the right place.
660 5 : gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);
661 : nsPoint toUserSpace =
662 5 : nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
663 10 : nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
664 :
665 5 : result.offsetToUserSpace = result.offsetToBoundingBox - toUserSpace;
666 :
667 : #ifdef DEBUG
668 5 : bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
669 5 : NS_ASSERTION(hasSVGLayout ||
670 : result.offsetToBoundingBox == result.offsetToUserSpace,
671 : "For non-SVG frames there shouldn't be any additional offset");
672 : #endif
673 :
674 : result.offsetToUserSpaceInDevPx =
675 : nsLayoutUtils::PointToGfxPoint(result.offsetToUserSpace,
676 5 : aFrame->PresContext()->AppUnitsPerDevPixel());
677 : result.offsetToBoundingBoxInDevPx =
678 : nsLayoutUtils::PointToGfxPoint(result.offsetToBoundingBox,
679 5 : aFrame->PresContext()->AppUnitsPerDevPixel());
680 :
681 5 : return result;
682 : }
683 :
684 : /**
685 : * Setup transform matrix of a gfx context by a specific frame. Move the
686 : * origin of aParams.ctx to the user space of aFrame.
687 : */
688 : static EffectOffsets
689 5 : MoveContextOriginToUserSpace(nsIFrame* aFrame, const PaintFramesParams& aParams)
690 : {
691 5 : EffectOffsets offset = ComputeEffectOffset(aFrame, aParams);
692 :
693 5 : aParams.ctx.SetMatrix(
694 10 : aParams.ctx.CurrentMatrix().PreTranslate(offset.offsetToUserSpaceInDevPx));
695 :
696 5 : return offset;
697 : }
698 :
699 : bool
700 0 : nsSVGIntegrationUtils::IsMaskResourceReady(nsIFrame* aFrame)
701 : {
702 : nsIFrame* firstFrame =
703 0 : nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
704 : nsSVGEffects::EffectProperties effectProperties =
705 0 : nsSVGEffects::GetEffectProperties(firstFrame);
706 0 : nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
707 0 : const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
708 :
709 0 : for (uint32_t i = 0; i < maskFrames.Length(); i++) {
710 : // Refers to a valid SVG mask.
711 0 : if (maskFrames[i]) {
712 0 : continue;
713 : }
714 :
715 : // Refers to an external resource, which is not ready yet.
716 0 : if (!svgReset->mMask.mLayers[i].mImage.IsComplete()) {
717 0 : return false;
718 : }
719 : }
720 :
721 : // Either all mask resources are ready, or no mask resource is needed.
722 0 : return true;
723 : }
724 :
725 : class AutoPopGroup
726 : {
727 : public:
728 0 : AutoPopGroup() : mContext(nullptr) { }
729 :
730 0 : ~AutoPopGroup() {
731 0 : if (mContext) {
732 0 : mContext->PopGroupAndBlend();
733 : }
734 0 : }
735 :
736 0 : void SetContext(gfxContext* aContext) {
737 0 : mContext = aContext;
738 0 : }
739 :
740 : private:
741 : gfxContext* mContext;
742 : };
743 :
744 : void
745 0 : nsSVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams)
746 : {
747 0 : nsSVGUtils::MaskUsage maskUsage;
748 0 : nsSVGUtils::DetermineMaskUsage(aParams.frame, aParams.handleOpacity,
749 0 : maskUsage);
750 :
751 0 : nsIFrame* frame = aParams.frame;
752 0 : if (!ValidateSVGFrame(frame)) {
753 0 : return;
754 : }
755 :
756 0 : gfxContext& ctx = aParams.ctx;
757 : nsIFrame* firstFrame =
758 0 : nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
759 : nsSVGEffects::EffectProperties effectProperties =
760 0 : nsSVGEffects::GetEffectProperties(firstFrame);
761 :
762 0 : RefPtr<DrawTarget> maskTarget = ctx.GetDrawTarget();
763 :
764 0 : if (maskUsage.shouldGenerateMaskLayer &&
765 0 : maskUsage.shouldGenerateClipMaskLayer) {
766 : // We will paint both mask of positioned mask and clip-path into
767 : // maskTarget.
768 : //
769 : // Create one extra draw target for drawing positioned mask, so that we do
770 : // not have to copy the content of maskTarget before painting
771 : // clip-path into it.
772 0 : maskTarget = maskTarget->CreateSimilarDrawTarget(maskTarget->GetSize(),
773 0 : SurfaceFormat::A8);
774 : }
775 :
776 0 : nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
777 0 : AutoPopGroup autoPop;
778 0 : bool shouldPushOpacity = (maskUsage.opacity != 1.0) &&
779 0 : (maskFrames.Length() != 1);
780 0 : if (shouldPushOpacity) {
781 0 : ctx.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, maskUsage.opacity);
782 0 : autoPop.SetContext(&ctx);
783 : }
784 :
785 0 : gfxContextMatrixAutoSaveRestore matSR;
786 :
787 : // Paint clip-path-basic-shape onto ctx
788 0 : gfxContextAutoSaveRestore basicShapeSR;
789 0 : if (maskUsage.shouldApplyBasicShape) {
790 0 : matSR.SetContext(&ctx);
791 :
792 0 : MoveContextOriginToUserSpace(firstFrame, aParams);
793 :
794 0 : basicShapeSR.SetContext(&ctx);
795 0 : nsCSSClipPathInstance::ApplyBasicShapeClip(ctx, frame);
796 0 : if (!maskUsage.shouldGenerateMaskLayer) {
797 : // Only have basic-shape clip-path effect. Fill clipped region by
798 : // opaque white.
799 0 : ctx.SetColor(Color(1.0, 1.0, 1.0, 1.0));
800 0 : ctx.Fill();
801 :
802 0 : return;
803 : }
804 : }
805 :
806 : // Paint mask onto ctx.
807 0 : if (maskUsage.shouldGenerateMaskLayer) {
808 0 : matSR.Restore();
809 0 : matSR.SetContext(&ctx);
810 :
811 0 : EffectOffsets offsets = MoveContextOriginToUserSpace(frame, aParams);
812 0 : PaintMaskSurface(aParams, maskTarget,
813 : shouldPushOpacity ? 1.0 : maskUsage.opacity,
814 : firstFrame->StyleContext(), maskFrames,
815 0 : ctx.CurrentMatrix(),
816 0 : offsets.offsetToUserSpace);
817 : }
818 :
819 : // Paint clip-path onto ctx.
820 0 : if (maskUsage.shouldGenerateClipMaskLayer || maskUsage.shouldApplyClipPath) {
821 0 : matSR.Restore();
822 0 : matSR.SetContext(&ctx);
823 :
824 0 : MoveContextOriginToUserSpace(firstFrame, aParams);
825 0 : Matrix clipMaskTransform;
826 0 : gfxMatrix cssPxToDevPxMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(frame);
827 :
828 0 : nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
829 : RefPtr<SourceSurface> maskSurface =
830 0 : maskUsage.shouldGenerateMaskLayer ? maskTarget->Snapshot() : nullptr;
831 0 : clipPathFrame->PaintClipMask(ctx, frame, cssPxToDevPxMatrix,
832 : &clipMaskTransform, maskSurface,
833 0 : ToMatrix(ctx.CurrentMatrix()));
834 : }
835 : }
836 :
837 : void
838 5 : nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
839 : {
840 5 : MOZ_ASSERT(UsingMaskOrClipPathForFrame(aParams.frame),
841 : "Should not use this method when no mask or clipPath effect"
842 : "on this frame");
843 :
844 : /* SVG defines the following rendering model:
845 : *
846 : * 1. Render geometry
847 : * 2. Apply filter
848 : * 3. Apply clipping, masking, group opacity
849 : *
850 : * We handle #3 here and perform a couple of optimizations:
851 : *
852 : * + Use cairo's clipPath when representable natively (single object
853 : * clip region).
854 : *
855 : * + Merge opacity and masking if both used together.
856 : */
857 5 : nsIFrame* frame = aParams.frame;
858 5 : if (!ValidateSVGFrame(frame)) {
859 0 : return;
860 : }
861 :
862 5 : nsSVGUtils::MaskUsage maskUsage;
863 5 : nsSVGUtils::DetermineMaskUsage(aParams.frame, aParams.handleOpacity,
864 5 : maskUsage);
865 :
866 5 : if (maskUsage.opacity == 0.0f) {
867 0 : return;
868 : }
869 :
870 5 : gfxContext& context = aParams.ctx;
871 10 : gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context);
872 :
873 : /* Properties are added lazily and may have been removed by a restyle,
874 : so make sure all applicable ones are set again. */
875 : nsIFrame* firstFrame =
876 5 : nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
877 : nsSVGEffects::EffectProperties effectProperties =
878 5 : nsSVGEffects::GetEffectProperties(firstFrame);
879 :
880 5 : nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
881 :
882 5 : gfxMatrix cssPxToDevPxMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(frame);
883 10 : nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
884 :
885 10 : bool shouldGenerateMask = (maskUsage.opacity != 1.0f ||
886 10 : maskUsage.shouldGenerateClipMaskLayer ||
887 10 : maskUsage.shouldGenerateMaskLayer);
888 5 : bool shouldPushMask = false;
889 :
890 : /* Check if we need to do additional operations on this child's
891 : * rendering, which necessitates rendering into another surface. */
892 5 : if (shouldGenerateMask) {
893 6 : gfxContextMatrixAutoSaveRestore matSR;
894 :
895 3 : Matrix maskTransform;
896 6 : RefPtr<SourceSurface> maskSurface;
897 3 : bool opacityApplied = false;
898 :
899 3 : if (maskUsage.shouldGenerateMaskLayer) {
900 3 : matSR.SetContext(&context);
901 :
902 : // For css-mask, we want to generate a mask for each continuation frame,
903 : // so we setup context matrix by the position of the current frame,
904 : // instead of the first continuation frame.
905 3 : EffectOffsets offsets = MoveContextOriginToUserSpace(frame, aParams);
906 : MaskPaintResult paintResult =
907 : CreateAndPaintMaskSurface(aParams, maskUsage.opacity,
908 : firstFrame->StyleContext(),
909 6 : maskFrames, offsets.offsetToUserSpace);
910 :
911 3 : if (paintResult.transparentBlackMask) {
912 0 : return;
913 : }
914 :
915 3 : maskSurface = paintResult.maskSurface;
916 3 : if (maskSurface) {
917 3 : shouldPushMask = true;
918 3 : maskTransform = paintResult.maskTransform;
919 3 : opacityApplied = paintResult.opacityApplied;
920 : }
921 : }
922 :
923 3 : if (maskUsage.shouldGenerateClipMaskLayer) {
924 0 : matSR.Restore();
925 0 : matSR.SetContext(&context);
926 :
927 0 : MoveContextOriginToUserSpace(firstFrame, aParams);
928 0 : Matrix clipMaskTransform;
929 : RefPtr<SourceSurface> clipMaskSurface =
930 0 : clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix,
931 : &clipMaskTransform, maskSurface,
932 0 : maskTransform);
933 :
934 0 : if (clipMaskSurface) {
935 0 : maskSurface = clipMaskSurface;
936 0 : maskTransform = clipMaskTransform;
937 : } else {
938 : // Either entire surface is clipped out, or gfx buffer allocation
939 : // failure in nsSVGClipPathFrame::GetClipMask.
940 0 : return;
941 : }
942 :
943 0 : shouldPushMask = true;
944 : }
945 :
946 : // opacity != 1.0f.
947 6 : if (!maskUsage.shouldGenerateClipMaskLayer &&
948 3 : !maskUsage.shouldGenerateMaskLayer) {
949 0 : MOZ_ASSERT(maskUsage.opacity != 1.0f);
950 :
951 0 : matSR.SetContext(&context);
952 0 : MoveContextOriginToUserSpace(firstFrame, aParams);
953 0 : shouldPushMask = true;
954 : }
955 :
956 3 : if (shouldPushMask) {
957 3 : if (aParams.layerManager->GetRoot()->GetContentFlags() &
958 : Layer::CONTENT_COMPONENT_ALPHA) {
959 3 : context.PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA,
960 : opacityApplied
961 : ? 1.0
962 : : maskUsage.opacity,
963 3 : maskSurface, maskTransform);
964 : } else {
965 0 : context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
966 : opacityApplied ? 1.0 : maskUsage.opacity,
967 0 : maskSurface, maskTransform);
968 : }
969 : }
970 : }
971 :
972 : /* If this frame has only a trivial clipPath, set up cairo's clipping now so
973 : * we can just do normal painting and get it clipped appropriately.
974 : */
975 5 : if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
976 4 : gfxContextMatrixAutoSaveRestore matSR(&context);
977 :
978 2 : MoveContextOriginToUserSpace(firstFrame, aParams);
979 :
980 2 : MOZ_ASSERT(!maskUsage.shouldApplyClipPath ||
981 : !maskUsage.shouldApplyBasicShape);
982 2 : if (maskUsage.shouldApplyClipPath) {
983 2 : clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix);
984 : } else {
985 0 : nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame);
986 : }
987 : }
988 :
989 : /* Paint the child */
990 5 : context.SetMatrix(matrixAutoSaveRestore.Matrix());
991 5 : BasicLayerManager* basic = aParams.layerManager->AsBasicLayerManager();
992 10 : RefPtr<gfxContext> oldCtx = basic->GetTarget();
993 5 : basic->SetTarget(&context);
994 10 : aParams.layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer,
995 10 : aParams.builder);
996 5 : basic->SetTarget(oldCtx);
997 :
998 5 : if (gfxPrefs::DrawMaskLayer()) {
999 0 : gfxContextAutoSaveRestore saver(&context);
1000 :
1001 0 : context.NewPath();
1002 : gfxRect drawingRect =
1003 : nsLayoutUtils::RectToGfxRect(aParams.borderArea,
1004 0 : frame->PresContext()->AppUnitsPerDevPixel());
1005 0 : context.Rectangle(drawingRect, true);
1006 0 : Color overlayColor(0.0f, 0.0f, 0.0f, 0.8f);
1007 0 : if (maskUsage.shouldGenerateMaskLayer) {
1008 0 : overlayColor.r = 1.0f; // red represents css positioned mask.
1009 : }
1010 0 : if (maskUsage.shouldApplyClipPath ||
1011 0 : maskUsage.shouldGenerateClipMaskLayer) {
1012 0 : overlayColor.g = 1.0f; // green represents clip-path:<clip-source>.
1013 : }
1014 0 : if (maskUsage.shouldApplyBasicShape) {
1015 0 : overlayColor.b = 1.0f; // blue represents
1016 : // clip-path:<basic-shape>||<geometry-box>.
1017 : }
1018 :
1019 0 : context.SetColor(overlayColor);
1020 0 : context.Fill();
1021 : }
1022 :
1023 5 : if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
1024 2 : context.PopClip();
1025 : }
1026 :
1027 5 : if (shouldPushMask) {
1028 3 : context.PopGroupAndBlend();
1029 : }
1030 :
1031 : }
1032 :
1033 : void
1034 0 : nsSVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams)
1035 : {
1036 0 : MOZ_ASSERT(!aParams.builder->IsForGenerateGlyphMask(),
1037 : "Filter effect is discarded while generating glyph mask.");
1038 0 : MOZ_ASSERT(aParams.frame->StyleEffects()->HasFilters(),
1039 : "Should not use this method when no filter effect on this frame");
1040 :
1041 0 : nsIFrame* frame = aParams.frame;
1042 0 : if (!ValidateSVGFrame(frame)) {
1043 0 : return;
1044 : }
1045 :
1046 0 : float opacity = nsSVGUtils::ComputeOpacity(frame, aParams.handleOpacity);
1047 0 : if (opacity == 0.0f) {
1048 0 : return;
1049 : }
1050 :
1051 : /* Properties are added lazily and may have been removed by a restyle,
1052 : so make sure all applicable ones are set again. */
1053 : nsIFrame* firstFrame =
1054 0 : nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
1055 : nsSVGEffects::EffectProperties effectProperties =
1056 0 : nsSVGEffects::GetEffectProperties(firstFrame);
1057 :
1058 0 : if (effectProperties.HasInvalidFilter()) {
1059 0 : return;
1060 : }
1061 :
1062 0 : gfxContext& context = aParams.ctx;
1063 :
1064 0 : gfxContextAutoSaveRestore autoSR(&context);
1065 0 : EffectOffsets offsets = MoveContextOriginToUserSpace(firstFrame, aParams);
1066 :
1067 0 : if (opacity != 1.0f) {
1068 : context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity,
1069 0 : nullptr, Matrix());
1070 : }
1071 :
1072 : /* Paint the child and apply filters */
1073 0 : RegularFramePaintCallback callback(aParams.builder, aParams.layerManager,
1074 0 : offsets.offsetToUserSpaceInDevPx);
1075 0 : nsRegion dirtyRegion = aParams.dirtyRect - offsets.offsetToBoundingBox;
1076 0 : gfxSize scaleFactors = context.CurrentMatrix().ScaleFactors(true);
1077 : gfxMatrix scaleMatrix(scaleFactors.width, 0.0f,
1078 : 0.0f, scaleFactors.height,
1079 0 : 0.0f, 0.0f);
1080 0 : gfxMatrix reverseScaleMatrix = scaleMatrix;
1081 0 : DebugOnly<bool> invertible = reverseScaleMatrix.Invert();
1082 0 : MOZ_ASSERT(invertible);
1083 0 : context.SetMatrix(reverseScaleMatrix * context.CurrentMatrix());
1084 :
1085 : gfxMatrix tm =
1086 0 : scaleMatrix * nsSVGUtils::GetCSSPxToDevPxMatrix(frame);
1087 0 : nsFilterInstance::PaintFilteredFrame(frame, context.GetDrawTarget(),
1088 : tm, &callback, &dirtyRegion,
1089 0 : aParams.imgParams);
1090 :
1091 0 : if (opacity != 1.0f) {
1092 0 : context.PopGroupAndBlend();
1093 : }
1094 : }
1095 :
1096 0 : class PaintFrameCallback : public gfxDrawingCallback {
1097 : public:
1098 0 : PaintFrameCallback(nsIFrame* aFrame,
1099 : const nsSize aPaintServerSize,
1100 : const IntSize aRenderSize,
1101 : uint32_t aFlags)
1102 0 : : mFrame(aFrame)
1103 : , mPaintServerSize(aPaintServerSize)
1104 : , mRenderSize(aRenderSize)
1105 0 : , mFlags (aFlags)
1106 0 : {}
1107 : virtual bool operator()(gfxContext* aContext,
1108 : const gfxRect& aFillRect,
1109 : const SamplingFilter aSamplingFilter,
1110 : const gfxMatrix& aTransform) override;
1111 : private:
1112 : nsIFrame* mFrame;
1113 : nsSize mPaintServerSize;
1114 : IntSize mRenderSize;
1115 : uint32_t mFlags;
1116 : };
1117 :
1118 : bool
1119 0 : PaintFrameCallback::operator()(gfxContext* aContext,
1120 : const gfxRect& aFillRect,
1121 : const SamplingFilter aSamplingFilter,
1122 : const gfxMatrix& aTransform)
1123 : {
1124 0 : if (mFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)
1125 0 : return false;
1126 :
1127 0 : AutoSetRestorePaintServerState paintServer(mFrame);
1128 :
1129 0 : aContext->Save();
1130 :
1131 : // Clip to aFillRect so that we don't paint outside.
1132 0 : aContext->NewPath();
1133 0 : aContext->Rectangle(aFillRect);
1134 0 : aContext->Clip();
1135 :
1136 0 : gfxMatrix invmatrix = aTransform;
1137 0 : if (!invmatrix.Invert()) {
1138 0 : return false;
1139 : }
1140 0 : aContext->Multiply(invmatrix);
1141 :
1142 : // nsLayoutUtils::PaintFrame will anchor its painting at mFrame. But we want
1143 : // to have it anchored at the top left corner of the bounding box of all of
1144 : // mFrame's continuations. So we add a translation transform.
1145 0 : int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
1146 0 : nsPoint offset = nsSVGIntegrationUtils::GetOffsetToBoundingBox(mFrame);
1147 0 : gfxPoint devPxOffset = gfxPoint(offset.x, offset.y) / appUnitsPerDevPixel;
1148 0 : aContext->Multiply(gfxMatrix::Translation(devPxOffset));
1149 :
1150 : gfxSize paintServerSize =
1151 0 : gfxSize(mPaintServerSize.width, mPaintServerSize.height) /
1152 0 : mFrame->PresContext()->AppUnitsPerDevPixel();
1153 :
1154 : // nsLayoutUtils::PaintFrame wants to render with paintServerSize, but we
1155 : // want it to render with mRenderSize, so we need to set up a scale transform.
1156 0 : gfxFloat scaleX = mRenderSize.width / paintServerSize.width;
1157 0 : gfxFloat scaleY = mRenderSize.height / paintServerSize.height;
1158 0 : aContext->Multiply(gfxMatrix::Scaling(scaleX, scaleY));
1159 :
1160 : // Draw.
1161 0 : nsRect dirty(-offset.x, -offset.y,
1162 0 : mPaintServerSize.width, mPaintServerSize.height);
1163 :
1164 : using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags;
1165 0 : PaintFrameFlags flags = PaintFrameFlags::PAINT_IN_TRANSFORM;
1166 0 : if (mFlags & nsSVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES) {
1167 0 : flags |= PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES;
1168 : }
1169 0 : nsLayoutUtils::PaintFrame(aContext, mFrame,
1170 : dirty, NS_RGBA(0, 0, 0, 0),
1171 : nsDisplayListBuilderMode::PAINTING,
1172 0 : flags);
1173 :
1174 0 : nsIFrame* currentFrame = mFrame;
1175 0 : while ((currentFrame = currentFrame->GetNextContinuation()) != nullptr) {
1176 0 : offset = currentFrame->GetOffsetToCrossDoc(mFrame);
1177 0 : devPxOffset = gfxPoint(offset.x, offset.y) / appUnitsPerDevPixel;
1178 :
1179 0 : aContext->Save();
1180 0 : aContext->Multiply(gfxMatrix::Scaling(1/scaleX, 1/scaleY));
1181 0 : aContext->Multiply(gfxMatrix::Translation(devPxOffset));
1182 0 : aContext->Multiply(gfxMatrix::Scaling(scaleX, scaleY));
1183 :
1184 0 : nsLayoutUtils::PaintFrame(aContext, currentFrame,
1185 0 : dirty - offset, NS_RGBA(0, 0, 0, 0),
1186 : nsDisplayListBuilderMode::PAINTING,
1187 0 : flags);
1188 :
1189 0 : aContext->Restore();
1190 : }
1191 :
1192 0 : aContext->Restore();
1193 :
1194 0 : return true;
1195 : }
1196 :
1197 : /* static */ already_AddRefed<gfxDrawable>
1198 0 : nsSVGIntegrationUtils::DrawableFromPaintServer(nsIFrame* aFrame,
1199 : nsIFrame* aTarget,
1200 : const nsSize& aPaintServerSize,
1201 : const IntSize& aRenderSize,
1202 : const DrawTarget* aDrawTarget,
1203 : const gfxMatrix& aContextMatrix,
1204 : uint32_t aFlags)
1205 : {
1206 : // aPaintServerSize is the size that would be filled when using
1207 : // background-repeat:no-repeat and background-size:auto. For normal background
1208 : // images, this would be the intrinsic size of the image; for gradients and
1209 : // patterns this would be the whole target frame fill area.
1210 : // aRenderSize is what we will be actually filling after accounting for
1211 : // background-size.
1212 0 : if (aFrame->IsFrameOfType(nsIFrame::eSVGPaintServer)) {
1213 : // aFrame is either a pattern or a gradient. These fill the whole target
1214 : // frame by default, so aPaintServerSize is the whole target background fill
1215 : // area.
1216 : nsSVGPaintServerFrame* server =
1217 0 : static_cast<nsSVGPaintServerFrame*>(aFrame);
1218 :
1219 : gfxRect overrideBounds(0, 0,
1220 0 : aPaintServerSize.width, aPaintServerSize.height);
1221 0 : overrideBounds.Scale(1.0 / aFrame->PresContext()->AppUnitsPerDevPixel());
1222 0 : imgDrawingParams imgParams(aFlags);
1223 : RefPtr<gfxPattern> pattern =
1224 0 : server->GetPaintServerPattern(aTarget, aDrawTarget,
1225 : aContextMatrix, &nsStyleSVG::mFill, 1.0,
1226 0 : imgParams, &overrideBounds);
1227 :
1228 0 : if (!pattern)
1229 0 : return nullptr;
1230 :
1231 : // pattern is now set up to fill aPaintServerSize. But we want it to
1232 : // fill aRenderSize, so we need to add a scaling transform.
1233 : // We couldn't just have set overrideBounds to aRenderSize - it would have
1234 : // worked for gradients, but for patterns it would result in a different
1235 : // pattern size.
1236 0 : gfxFloat scaleX = overrideBounds.Width() / aRenderSize.width;
1237 0 : gfxFloat scaleY = overrideBounds.Height() / aRenderSize.height;
1238 0 : gfxMatrix scaleMatrix = gfxMatrix::Scaling(scaleX, scaleY);
1239 0 : pattern->SetMatrix(scaleMatrix * pattern->GetMatrix());
1240 : RefPtr<gfxDrawable> drawable =
1241 0 : new gfxPatternDrawable(pattern, aRenderSize);
1242 0 : return drawable.forget();
1243 : }
1244 :
1245 0 : if (aFrame->IsFrameOfType(nsIFrame::eSVG) &&
1246 0 : !static_cast<nsSVGDisplayableFrame*>(do_QueryFrame(aFrame))) {
1247 0 : MOZ_ASSERT_UNREACHABLE("We should prevent painting of unpaintable SVG "
1248 : "before we get here");
1249 : return nullptr;
1250 : }
1251 :
1252 : // We don't want to paint into a surface as long as we don't need to, so we
1253 : // set up a drawing callback.
1254 : RefPtr<gfxDrawingCallback> cb =
1255 0 : new PaintFrameCallback(aFrame, aPaintServerSize, aRenderSize, aFlags);
1256 0 : RefPtr<gfxDrawable> drawable = new gfxCallbackDrawable(cb, aRenderSize);
1257 0 : return drawable.forget();
1258 : }
|