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/. */
7 #include "nsSVGIntegrationUtils.h"
9 // Keep others in (case-insensitive) order:
10 #include "gfxDrawable.h"
11 #include "nsCSSAnonBoxes.h"
12 #include "nsDisplayList.h"
13 #include "nsFilterInstance.h"
14 #include "nsLayoutUtils.h"
15 #include "nsRenderingContext.h"
16 #include "nsSVGClipPathFrame.h"
17 #include "nsSVGEffects.h"
18 #include "nsSVGElement.h"
19 #include "nsSVGFilterPaintCallback.h"
20 #include "nsSVGMaskFrame.h"
21 #include "nsSVGPaintServerFrame.h"
22 #include "nsSVGUtils.h"
23 #include "FrameLayerBuilder.h"
24 #include "BasicLayers.h"
25 #include "mozilla/gfx/Point.h"
27 using namespace mozilla
;
28 using namespace mozilla::layers
;
30 // ----------------------------------------------------------------------
33 * This class is used to get the pre-effects visual overflow rect of a frame,
34 * or, in the case of a frame with continuations, to collect the union of the
35 * pre-effects visual overflow rects of all the continuations. The result is
36 * relative to the origin (top left corner of the border box) of the frame, or,
37 * if the frame has continuations, the origin of the _first_ continuation.
39 class PreEffectsVisualOverflowCollector
: public nsLayoutUtils::BoxCallback
43 * If the pre-effects visual overflow rect of the frame being examined
44 * happens to be known, it can be passed in as aCurrentFrame and its
45 * pre-effects visual overflow rect can be passed in as
46 * aCurrentFrameOverflowArea. This is just an optimization to save a
47 * frame property lookup - these arguments are optional.
49 PreEffectsVisualOverflowCollector(nsIFrame
* aFirstContinuation
,
50 nsIFrame
* aCurrentFrame
,
51 const nsRect
& aCurrentFrameOverflowArea
)
52 : mFirstContinuation(aFirstContinuation
)
53 , mCurrentFrame(aCurrentFrame
)
54 , mCurrentFrameOverflowArea(aCurrentFrameOverflowArea
)
56 NS_ASSERTION(!mFirstContinuation
->GetPrevContinuation(),
57 "We want the first continuation here");
60 virtual void AddBox(nsIFrame
* aFrame
) MOZ_OVERRIDE
{
61 nsRect overflow
= (aFrame
== mCurrentFrame
) ?
62 mCurrentFrameOverflowArea
: GetPreEffectsVisualOverflowRect(aFrame
);
63 mResult
.UnionRect(mResult
, overflow
+ aFrame
->GetOffsetTo(mFirstContinuation
));
66 nsRect
GetResult() const {
72 static nsRect
GetPreEffectsVisualOverflowRect(nsIFrame
* aFrame
) {
73 nsRect
* r
= static_cast<nsRect
*>
74 (aFrame
->Properties().Get(nsIFrame::PreEffectsBBoxProperty()));
78 // Despite the fact that we're invoked for frames with SVG effects applied,
79 // we can actually get here. All continuations and IB split siblings of a
80 // frame with SVG effects applied will have the PreEffectsBBoxProperty
81 // property set on them. Therefore, the frames that are passed to us will
82 // always have that property set...well, with one exception. If the frames
83 // for an element with SVG effects applied have been subject to an "IB
84 // split", then the block frame(s) that caused the split will have been
85 // wrapped in anonymous, inline-block, nsBlockFrames of pseudo-type
86 // nsCSSAnonBoxes::mozAnonymousBlock. These "IB split sibling" anonymous
87 // blocks will have the PreEffectsBBoxProperty property set on them, but
88 // they will never be passed to us. Instead, we'll be passed the block
89 // children that they wrap, which don't have the PreEffectsBBoxProperty
90 // property set on them. This is actually okay. What we care about is
91 // collecting the _pre_ effects visual overflow rects of the frames to
92 // which the SVG effects have been applied. Since the IB split results in
93 // any overflow rect adjustments for transforms, effects, etc. taking
94 // place on the anonymous block wrappers, the wrapped children are left
95 // with their overflow rects unaffected. In other words, calling
96 // GetVisualOverflowRect() on the children will return their pre-effects
97 // visual overflow rects, just as we need.
99 // A couple of tests that demonstrate the IB split and cause us to get here
102 // * reftests/svg/svg-integration/clipPath-html-06.xhtml
103 // * reftests/svg/svg-integration/clipPath-html-06-extref.xhtml
105 // If we ever got passed a frame with the PreTransformOverflowAreasProperty
106 // property set, that would be bad, since then our GetVisualOverflowRect()
107 // call would give us the post-effects, and post-transform, overflow rect.
109 NS_ASSERTION(aFrame
->GetParent()->StyleContext()->GetPseudo() ==
110 nsCSSAnonBoxes::mozAnonymousBlock
,
111 "How did we getting here, then?");
112 NS_ASSERTION(!aFrame
->Properties().Get(
113 aFrame
->PreTransformOverflowAreasProperty()),
114 "GetVisualOverflowRect() won't return the pre-effects rect!");
115 return aFrame
->GetVisualOverflowRect();
118 nsIFrame
* mFirstContinuation
;
119 nsIFrame
* mCurrentFrame
;
120 const nsRect
& mCurrentFrameOverflowArea
;
125 * Gets the union of the pre-effects visual overflow rects of all of a frame's
126 * continuations, in "user space".
129 GetPreEffectsVisualOverflowUnion(nsIFrame
* aFirstContinuation
,
130 nsIFrame
* aCurrentFrame
,
131 const nsRect
& aCurrentFramePreEffectsOverflow
,
132 const nsPoint
& aFirstContinuationToUserSpace
)
134 NS_ASSERTION(!aFirstContinuation
->GetPrevContinuation(),
135 "Need first continuation here");
136 PreEffectsVisualOverflowCollector
collector(aFirstContinuation
,
138 aCurrentFramePreEffectsOverflow
);
139 // Compute union of all overflow areas relative to aFirstContinuation:
140 nsLayoutUtils::GetAllInFlowBoxes(aFirstContinuation
, &collector
);
141 // Return the result in user space:
142 return collector
.GetResult() + aFirstContinuationToUserSpace
;
147 nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame
* aFrame
)
149 // Even when SVG display lists are disabled, returning true for SVG frames
150 // does not adversely affect any of our callers. Therefore we don't bother
151 // checking the SDL prefs here, since we don't know if we're being called for
152 // painting or hit-testing anyway.
153 const nsStyleSVGReset
*style
= aFrame
->StyleSVGReset();
154 return (style
->HasFilters() || style
->mClipPath
|| style
->mMask
);
157 // For non-SVG frames, this gives the offset to the frame's "user space".
158 // For SVG frames, this returns a zero offset.
160 GetOffsetToBoundingBox(nsIFrame
* aFrame
)
162 if ((aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
)) {
163 // Do NOT call GetAllInFlowRectsUnion for SVG - it will get the
164 // covered region relative to the nsSVGOuterSVGFrame, which is absolutely
165 // not what we want. SVG frames are always in user space, so they have
166 // no offset adjustment to make.
169 // We could allow aFrame to be any continuation, but since that would require
170 // a GetPrevContinuation() virtual call and conditional returns, and since
171 // all our current consumers always pass in the first continuation, we don't
173 NS_ASSERTION(!aFrame
->GetPrevContinuation(), "Not first continuation");
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();
182 nsSVGIntegrationUtils::GetContinuationUnionSize(nsIFrame
* aNonSVGFrame
)
184 NS_ASSERTION(!aNonSVGFrame
->IsFrameOfType(nsIFrame::eSVG
),
185 "SVG frames should not get here");
186 nsIFrame
* firstFrame
=
187 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame
);
188 return nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame
, firstFrame
).Size();
191 /* static */ gfx::Size
192 nsSVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(nsIFrame
* aNonSVGFrame
)
194 NS_ASSERTION(!aNonSVGFrame
->IsFrameOfType(nsIFrame::eSVG
),
195 "SVG frames should not get here");
196 nsIFrame
* firstFrame
=
197 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame
);
198 nsRect r
= nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame
, firstFrame
);
199 nsPresContext
* presContext
= firstFrame
->PresContext();
200 return gfx::Size(presContext
->AppUnitsToFloatCSSPixels(r
.width
),
201 presContext
->AppUnitsToFloatCSSPixels(r
.height
));
205 nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(nsIFrame
* aNonSVGFrame
)
207 NS_ASSERTION(!aNonSVGFrame
->IsFrameOfType(nsIFrame::eSVG
),
208 "SVG frames should not get here");
209 nsIFrame
* firstFrame
=
210 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame
);
211 // 'r' is in "user space":
212 nsRect r
= GetPreEffectsVisualOverflowUnion(firstFrame
, nullptr, nsRect(),
213 GetOffsetToBoundingBox(firstFrame
));
214 return nsLayoutUtils::RectToGfxRect(r
,
215 aNonSVGFrame
->PresContext()->AppUnitsPerCSSPixel());
218 // XXX Since we're called during reflow, this method is broken for frames with
219 // continuations. When we're called for a frame with continuations, we're
220 // called for each continuation in turn as it's reflowed. However, it isn't
221 // until the last continuation is reflowed that this method's
222 // GetOffsetToBoundingBox() and GetPreEffectsVisualOverflowUnion() calls will
223 // obtain valid border boxes for all the continuations. As a result, we'll
224 // end up returning bogus post-filter visual overflow rects for all the prior
225 // continuations. Unfortunately, by the time the last continuation is
226 // reflowed, it's too late to go back and set and propagate the overflow
227 // rects on the previous continuations.
229 // The reason that we need to pass an override bbox to
230 // GetPreEffectsVisualOverflowUnion rather than just letting it call into our
231 // GetSVGBBoxForNonSVGFrame method is because we get called by
232 // ComputeEffectsRect when it has been called with
233 // aStoreRectProperties set to false. In this case the pre-effects visual
234 // overflow rect that it has been passed may be different to that stored on
235 // aFrame, resulting in a different bbox.
237 // XXXjwatt The pre-effects visual overflow rect passed to
238 // ComputeEffectsRect won't include continuation overflows, so
239 // for frames with continuation the following filter analysis will likely end
240 // up being carried out with a bbox created as if the frame didn't have
243 // XXXjwatt Using aPreEffectsOverflowRect to create the bbox isn't really right
244 // for SVG frames, since for SVG frames the SVG spec defines the bbox to be
245 // something quite different to the pre-effects visual overflow rect. However,
246 // we're essentially calculating an invalidation area here, and using the
247 // pre-effects overflow rect will actually overestimate that area which, while
248 // being a bit wasteful, isn't otherwise a problem.
251 nsSVGIntegrationUtils::
252 ComputePostEffectsVisualOverflowRect(nsIFrame
* aFrame
,
253 const nsRect
& aPreEffectsOverflowRect
)
255 NS_ASSERTION(!(aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
),
256 "Don't call this on SVG child frames");
258 nsIFrame
* firstFrame
=
259 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame
);
260 nsSVGEffects::EffectProperties effectProperties
=
261 nsSVGEffects::GetEffectProperties(firstFrame
);
262 if (!effectProperties
.HasValidFilter()) {
263 return aPreEffectsOverflowRect
;
266 // Create an override bbox - see comment above:
267 nsPoint firstFrameToBoundingBox
= GetOffsetToBoundingBox(firstFrame
);
268 // overrideBBox is in "user space", in _CSS_ pixels:
269 // XXX Why are we rounding out to pixel boundaries? We don't do that in
270 // GetSVGBBoxForNonSVGFrame, and it doesn't appear to be necessary.
271 gfxRect overrideBBox
=
272 nsLayoutUtils::RectToGfxRect(
273 GetPreEffectsVisualOverflowUnion(firstFrame
, aFrame
,
274 aPreEffectsOverflowRect
,
275 firstFrameToBoundingBox
),
276 aFrame
->PresContext()->AppUnitsPerCSSPixel());
277 overrideBBox
.RoundOut();
279 nsRect overflowRect
=
280 nsFilterInstance::GetPostFilterBounds(firstFrame
, &overrideBBox
);
282 // Return overflowRect relative to aFrame, rather than "user space":
283 return overflowRect
- (aFrame
->GetOffsetTo(firstFrame
) + firstFrameToBoundingBox
);
287 nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame
* aFrame
,
288 const nsPoint
& aToReferenceFrame
,
289 const nsIntRegion
& aInvalidRegion
)
291 if (aInvalidRegion
.IsEmpty()) {
295 // Don't bother calling GetEffectProperties; the filter property should
296 // already have been set up during reflow/ComputeFrameEffectsRect
297 nsIFrame
* firstFrame
=
298 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame
);
299 nsSVGFilterProperty
*prop
= nsSVGEffects::GetFilterProperty(firstFrame
);
300 if (!prop
|| !prop
->IsInObserverLists()) {
301 return aInvalidRegion
;
304 int32_t appUnitsPerDevPixel
= aFrame
->PresContext()->AppUnitsPerDevPixel();
306 if (!prop
|| !prop
->ReferencesValidResources()) {
307 // The frame is either not there or not currently available,
308 // perhaps because we're in the middle of tearing stuff down.
309 // Be conservative, return our visual overflow rect relative
310 // to the reference frame.
311 nsRect overflow
= aFrame
->GetVisualOverflowRect() + aToReferenceFrame
;
312 return overflow
.ToOutsidePixels(appUnitsPerDevPixel
);
315 // Convert aInvalidRegion into bounding box frame space in app units:
316 nsPoint toBoundingBox
=
317 aFrame
->GetOffsetTo(firstFrame
) + GetOffsetToBoundingBox(firstFrame
);
318 // The initial rect was relative to the reference frame, so we need to
319 // remove that offset to get a rect relative to the current frame.
320 toBoundingBox
-= aToReferenceFrame
;
321 nsRegion preEffectsRegion
= aInvalidRegion
.ToAppUnits(appUnitsPerDevPixel
).MovedBy(toBoundingBox
);
323 // Adjust the dirty area for effects, and shift it back to being relative to
324 // the reference frame.
325 nsRegion result
= nsFilterInstance::GetPostFilterDirtyArea(firstFrame
,
326 preEffectsRegion
).MovedBy(-toBoundingBox
);
327 // Return the result, in pixels relative to the reference frame.
328 return result
.ToOutsidePixels(appUnitsPerDevPixel
);
332 nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame
* aFrame
,
333 const nsRect
& aDirtyRect
)
335 // Don't bother calling GetEffectProperties; the filter property should
336 // already have been set up during reflow/ComputeFrameEffectsRect
337 nsIFrame
* firstFrame
=
338 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame
);
339 nsSVGFilterProperty
*prop
= nsSVGEffects::GetFilterProperty(firstFrame
);
340 if (!prop
|| !prop
->ReferencesValidResources()) {
344 // Convert aDirtyRect into "user space" in app units:
345 nsPoint toUserSpace
=
346 aFrame
->GetOffsetTo(firstFrame
) + GetOffsetToBoundingBox(firstFrame
);
347 nsRect postEffectsRect
= aDirtyRect
+ toUserSpace
;
349 // Return ther result, relative to aFrame, not in user space:
350 return nsFilterInstance::GetPreFilterNeededArea(firstFrame
, postEffectsRect
).GetBounds()
355 nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame
* aFrame
, const nsPoint
& aPt
)
357 nsIFrame
* firstFrame
=
358 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame
);
359 // Convert aPt to user space:
361 if (aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
) {
362 // XXXmstange Isn't this wrong for svg:use and innerSVG frames?
363 toUserSpace
= aFrame
->GetPosition();
366 aFrame
->GetOffsetTo(firstFrame
) + GetOffsetToBoundingBox(firstFrame
);
368 nsPoint pt
= aPt
+ toUserSpace
;
369 gfxPoint userSpacePt
=
370 gfxPoint(pt
.x
, pt
.y
) / aFrame
->PresContext()->AppUnitsPerCSSPixel();
371 return nsSVGUtils::HitTestClip(firstFrame
, userSpacePt
);
374 class RegularFramePaintCallback
: public nsSVGFilterPaintCallback
377 RegularFramePaintCallback(nsDisplayListBuilder
* aBuilder
,
378 LayerManager
* aManager
,
379 const nsPoint
& aOffset
)
380 : mBuilder(aBuilder
), mLayerManager(aManager
),
383 virtual void Paint(nsRenderingContext
*aContext
, nsIFrame
*aTarget
,
384 const nsIntRect
* aDirtyRect
,
385 nsIFrame
* aTransformRoot
) MOZ_OVERRIDE
387 BasicLayerManager
* basic
= static_cast<BasicLayerManager
*>(mLayerManager
);
388 basic
->SetTarget(aContext
->ThebesContext());
389 nsRenderingContext::AutoPushTranslation
push(aContext
, -mOffset
);
390 mLayerManager
->EndTransaction(FrameLayerBuilder::DrawThebesLayer
, mBuilder
);
394 nsDisplayListBuilder
* mBuilder
;
395 LayerManager
* mLayerManager
;
400 nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext
* aCtx
,
402 const nsRect
& aDirtyRect
,
403 nsDisplayListBuilder
* aBuilder
,
404 LayerManager
*aLayerManager
)
407 NS_ASSERTION(!(aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
) ||
408 (NS_SVGDisplayListPaintingEnabled() &&
409 !(aFrame
->GetStateBits() & NS_FRAME_IS_NONDISPLAY
)),
410 "Should not use nsSVGIntegrationUtils on this SVG frame");
413 /* SVG defines the following rendering model:
417 * 3. Apply clipping, masking, group opacity
419 * We follow this, but perform a couple of optimizations:
421 * + Use cairo's clipPath when representable natively (single object
424 * + Merge opacity and masking if both used together.
427 const nsIContent
* content
= aFrame
->GetContent();
428 bool hasSVGLayout
= (aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
);
430 nsISVGChildFrame
*svgChildFrame
= do_QueryFrame(aFrame
);
431 if (!svgChildFrame
|| !aFrame
->GetContent()->IsSVG()) {
432 NS_ASSERTION(false, "why?");
435 if (!static_cast<const nsSVGElement
*>(content
)->HasValidDimensions()) {
436 return; // The SVG spec says not to draw _anything_
440 float opacity
= aFrame
->StyleDisplay()->mOpacity
;
441 if (opacity
== 0.0f
) {
444 if (opacity
!= 1.0f
&&
445 hasSVGLayout
&& nsSVGUtils::CanOptimizeOpacity(aFrame
)) {
449 /* Properties are added lazily and may have been removed by a restyle,
450 so make sure all applicable ones are set again. */
451 nsIFrame
* firstFrame
=
452 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame
);
453 nsSVGEffects::EffectProperties effectProperties
=
454 nsSVGEffects::GetEffectProperties(firstFrame
);
456 bool isOK
= effectProperties
.HasNoFilterOrHasValidFilter();
457 nsSVGClipPathFrame
*clipPathFrame
= effectProperties
.GetClipPathFrame(&isOK
);
458 nsSVGMaskFrame
*maskFrame
= effectProperties
.GetMaskFrame(&isOK
);
460 return; // Some resource is missing. We shouldn't paint anything.
463 bool isTrivialClip
= clipPathFrame
? clipPathFrame
->IsTrivial() : true;
465 gfxContext
* gfx
= aCtx
->ThebesContext();
466 gfxContextMatrixAutoSaveRestore
matrixAutoSaveRestore(gfx
);
468 nsPoint firstFrameOffset
= GetOffsetToBoundingBox(firstFrame
);
469 nsPoint offsetToBoundingBox
= aBuilder
->ToReferenceFrame(firstFrame
) - firstFrameOffset
;
470 if (!firstFrame
->IsFrameOfType(nsIFrame::eSVG
)) {
471 /* Snap the offset if the reference frame is not a SVG frame,
472 * since other frames will be snapped to pixel when rendering. */
473 offsetToBoundingBox
= nsPoint(
474 aFrame
->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox
.x
),
475 aFrame
->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox
.y
));
478 // After applying only "offsetToBoundingBox", aCtx would have its origin at
479 // the top left corner of aFrame's bounding box (over all continuations).
480 // However, SVG painting needs the origin to be located at the origin of the
481 // SVG frame's "user space", i.e. the space in which, for example, the
482 // frame's BBox lives.
483 // SVG geometry frames and foreignObject frames apply their own offsets, so
484 // their position is relative to their user space. So for these frame types,
485 // if we want aCtx to be in user space, we first need to subtract the
486 // frame's position so that SVG painting can later add it again and the
487 // frame is painted in the right place.
489 gfxPoint toUserSpaceGfx
= nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame
);
490 nsPoint
toUserSpace(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx
.x
)),
491 nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx
.y
)));
492 nsPoint offsetToUserSpace
= offsetToBoundingBox
- toUserSpace
;
494 NS_ASSERTION(hasSVGLayout
|| offsetToBoundingBox
== offsetToUserSpace
,
495 "For non-SVG frames there shouldn't be any additional offset");
497 aCtx
->Translate(offsetToUserSpace
);
499 gfxMatrix cssPxToDevPxMatrix
= GetCSSPxToDevPxMatrix(aFrame
);
501 bool complexEffects
= false;
502 /* Check if we need to do additional operations on this child's
503 * rendering, which necessitates rendering into another surface. */
504 if (opacity
!= 1.0f
|| maskFrame
|| (clipPathFrame
&& !isTrivialClip
)
505 || aFrame
->StyleDisplay()->mMixBlendMode
!= NS_STYLE_BLEND_NORMAL
) {
506 complexEffects
= true;
508 aCtx
->IntersectClip(aFrame
->GetVisualOverflowRectRelativeToSelf() +
510 gfx
->PushGroup(gfxContentType::COLOR_ALPHA
);
513 /* If this frame has only a trivial clipPath, set up cairo's clipping now so
514 * we can just do normal painting and get it clipped appropriately.
516 if (clipPathFrame
&& isTrivialClip
) {
518 clipPathFrame
->ApplyClipOrPaintClipMask(aCtx
, aFrame
, cssPxToDevPxMatrix
);
521 /* Paint the child */
522 if (effectProperties
.HasValidFilter()) {
523 RegularFramePaintCallback
callback(aBuilder
, aLayerManager
,
526 nsRegion dirtyRegion
= aDirtyRect
- offsetToBoundingBox
;
527 nsFilterInstance::PaintFilteredFrame(aCtx
, aFrame
, &callback
, &dirtyRegion
);
529 gfx
->SetMatrix(matrixAutoSaveRestore
.Matrix());
530 aLayerManager
->EndTransaction(FrameLayerBuilder::DrawThebesLayer
, aBuilder
);
531 aCtx
->Translate(offsetToUserSpace
);
534 if (clipPathFrame
&& isTrivialClip
) {
538 /* No more effects, we're done. */
539 if (!complexEffects
) {
543 gfx
->PopGroupToSource();
545 nsRefPtr
<gfxPattern
> maskSurface
=
546 maskFrame
? maskFrame
->GetMaskForMaskedFrame(aCtx
->ThebesContext(),
547 aFrame
, cssPxToDevPxMatrix
,
551 nsRefPtr
<gfxPattern
> clipMaskSurface
;
552 if (clipPathFrame
&& !isTrivialClip
) {
553 gfx
->PushGroup(gfxContentType::COLOR_ALPHA
);
555 nsresult rv
= clipPathFrame
->ApplyClipOrPaintClipMask(aCtx
, aFrame
, cssPxToDevPxMatrix
);
556 clipMaskSurface
= gfx
->PopGroup();
558 if (NS_SUCCEEDED(rv
) && clipMaskSurface
) {
559 // Still more set after clipping, so clip to another surface
560 if (maskSurface
|| opacity
!= 1.0f
) {
561 gfx
->PushGroup(gfxContentType::COLOR_ALPHA
);
562 gfx
->Mask(clipMaskSurface
);
563 gfx
->PopGroupToSource();
565 gfx
->Mask(clipMaskSurface
);
571 gfx
->Mask(maskSurface
);
572 } else if (opacity
!= 1.0f
) {
580 nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(nsIFrame
* aNonSVGFrame
)
582 int32_t appUnitsPerDevPixel
= aNonSVGFrame
->PresContext()->AppUnitsPerDevPixel();
583 float devPxPerCSSPx
=
584 1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel
);
586 return gfxMatrix(devPxPerCSSPx
, 0.0,
591 class PaintFrameCallback
: public gfxDrawingCallback
{
593 PaintFrameCallback(nsIFrame
* aFrame
,
594 const nsSize aPaintServerSize
,
595 const gfxIntSize aRenderSize
,
598 , mPaintServerSize(aPaintServerSize
)
599 , mRenderSize(aRenderSize
)
602 virtual bool operator()(gfxContext
* aContext
,
603 const gfxRect
& aFillRect
,
604 const GraphicsFilter
& aFilter
,
605 const gfxMatrix
& aTransform
) MOZ_OVERRIDE
;
608 nsSize mPaintServerSize
;
609 gfxIntSize mRenderSize
;
614 PaintFrameCallback::operator()(gfxContext
* aContext
,
615 const gfxRect
& aFillRect
,
616 const GraphicsFilter
& aFilter
,
617 const gfxMatrix
& aTransform
)
619 if (mFrame
->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER
)
622 mFrame
->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER
);
624 nsRefPtr
<nsRenderingContext
> context(new nsRenderingContext());
625 context
->Init(mFrame
->PresContext()->DeviceContext(), aContext
);
628 // Clip to aFillRect so that we don't paint outside.
630 aContext
->Rectangle(aFillRect
);
633 gfxMatrix invmatrix
= aTransform
;
634 if (!invmatrix
.Invert()) {
637 aContext
->Multiply(invmatrix
);
639 // nsLayoutUtils::PaintFrame will anchor its painting at mFrame. But we want
640 // to have it anchored at the top left corner of the bounding box of all of
641 // mFrame's continuations. So we add a translation transform.
642 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
643 nsPoint offset
= GetOffsetToBoundingBox(mFrame
);
644 gfxPoint devPxOffset
= gfxPoint(offset
.x
, offset
.y
) / appUnitsPerDevPixel
;
645 aContext
->Multiply(gfxMatrix().Translate(devPxOffset
));
647 gfxSize paintServerSize
=
648 gfxSize(mPaintServerSize
.width
, mPaintServerSize
.height
) /
649 mFrame
->PresContext()->AppUnitsPerDevPixel();
651 // nsLayoutUtils::PaintFrame wants to render with paintServerSize, but we
652 // want it to render with mRenderSize, so we need to set up a scale transform.
653 gfxFloat scaleX
= mRenderSize
.width
/ paintServerSize
.width
;
654 gfxFloat scaleY
= mRenderSize
.height
/ paintServerSize
.height
;
655 gfxMatrix scaleMatrix
= gfxMatrix().Scale(scaleX
, scaleY
);
656 aContext
->Multiply(scaleMatrix
);
659 nsRect
dirty(-offset
.x
, -offset
.y
,
660 mPaintServerSize
.width
, mPaintServerSize
.height
);
662 uint32_t flags
= nsLayoutUtils::PAINT_IN_TRANSFORM
|
663 nsLayoutUtils::PAINT_ALL_CONTINUATIONS
;
664 if (mFlags
& nsSVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES
) {
665 flags
|= nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES
;
667 nsLayoutUtils::PaintFrame(context
, mFrame
,
668 dirty
, NS_RGBA(0, 0, 0, 0),
673 mFrame
->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER
);
678 /* static */ already_AddRefed
<gfxDrawable
>
679 nsSVGIntegrationUtils::DrawableFromPaintServer(nsIFrame
* aFrame
,
681 const nsSize
& aPaintServerSize
,
682 const gfxIntSize
& aRenderSize
,
683 const gfxMatrix
& aContextMatrix
,
686 // aPaintServerSize is the size that would be filled when using
687 // background-repeat:no-repeat and background-size:auto. For normal background
688 // images, this would be the intrinsic size of the image; for gradients and
689 // patterns this would be the whole target frame fill area.
690 // aRenderSize is what we will be actually filling after accounting for
692 if (aFrame
->IsFrameOfType(nsIFrame::eSVGPaintServer
)) {
693 // aFrame is either a pattern or a gradient. These fill the whole target
694 // frame by default, so aPaintServerSize is the whole target background fill
696 nsSVGPaintServerFrame
* server
=
697 static_cast<nsSVGPaintServerFrame
*>(aFrame
);
699 gfxRect
overrideBounds(0, 0,
700 aPaintServerSize
.width
, aPaintServerSize
.height
);
701 overrideBounds
.ScaleInverse(aFrame
->PresContext()->AppUnitsPerDevPixel());
702 nsRefPtr
<gfxPattern
> pattern
=
703 server
->GetPaintServerPattern(aTarget
, aContextMatrix
,
704 &nsStyleSVG::mFill
, 1.0, &overrideBounds
);
709 // pattern is now set up to fill aPaintServerSize. But we want it to
710 // fill aRenderSize, so we need to add a scaling transform.
711 // We couldn't just have set overrideBounds to aRenderSize - it would have
712 // worked for gradients, but for patterns it would result in a different
714 gfxFloat scaleX
= overrideBounds
.Width() / aRenderSize
.width
;
715 gfxFloat scaleY
= overrideBounds
.Height() / aRenderSize
.height
;
716 gfxMatrix scaleMatrix
= gfxMatrix::Scaling(scaleX
, scaleY
);
717 pattern
->SetMatrix(scaleMatrix
* pattern
->GetMatrix());
718 nsRefPtr
<gfxDrawable
> drawable
=
719 new gfxPatternDrawable(pattern
, aRenderSize
);
720 return drawable
.forget();
723 // We don't want to paint into a surface as long as we don't need to, so we
724 // set up a drawing callback.
725 nsRefPtr
<gfxDrawingCallback
> cb
=
726 new PaintFrameCallback(aFrame
, aPaintServerSize
, aRenderSize
, aFlags
);
727 nsRefPtr
<gfxDrawable
> drawable
= new gfxCallbackDrawable(cb
, aRenderSize
);
728 return drawable
.forget();