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 "nsLayoutUtils.h"
14 #include "nsRenderingContext.h"
15 #include "nsSVGClipPathFrame.h"
16 #include "nsSVGEffects.h"
17 #include "nsSVGElement.h"
18 #include "nsSVGFilterFrame.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"
26 using namespace mozilla
;
27 using namespace mozilla::layers
;
29 // ----------------------------------------------------------------------
32 * This class is used to get the pre-effects visual overflow rect of a frame,
33 * or, in the case of a frame with continuations, to collect the union of the
34 * pre-effects visual overflow rects of all the continuations. The result is
35 * relative to the origin (top left corner of the border box) of the frame, or,
36 * if the frame has continuations, the origin of the _first_ continuation.
38 class PreEffectsVisualOverflowCollector
: public nsLayoutUtils::BoxCallback
42 * If the pre-effects visual overflow rect of the frame being examined
43 * happens to be known, it can be passed in as aCurrentFrame and its
44 * pre-effects visual overflow rect can be passed in as
45 * aCurrentFrameOverflowArea. This is just an optimization to save a
46 * frame property lookup - these arguments are optional.
48 PreEffectsVisualOverflowCollector(nsIFrame
* aFirstContinuation
,
49 nsIFrame
* aCurrentFrame
,
50 const nsRect
& aCurrentFrameOverflowArea
)
51 : mFirstContinuation(aFirstContinuation
)
52 , mCurrentFrame(aCurrentFrame
)
53 , mCurrentFrameOverflowArea(aCurrentFrameOverflowArea
)
55 NS_ASSERTION(!mFirstContinuation
->GetPrevContinuation(),
56 "We want the first continuation here");
59 virtual void AddBox(nsIFrame
* aFrame
) {
60 nsRect overflow
= (aFrame
== mCurrentFrame
) ?
61 mCurrentFrameOverflowArea
: GetPreEffectsVisualOverflowRect(aFrame
);
62 mResult
.UnionRect(mResult
, overflow
+ aFrame
->GetOffsetTo(mFirstContinuation
));
65 nsRect
GetResult() const {
71 static nsRect
GetPreEffectsVisualOverflowRect(nsIFrame
* aFrame
) {
72 nsRect
* r
= static_cast<nsRect
*>
73 (aFrame
->Properties().Get(nsIFrame::PreEffectsBBoxProperty()));
77 // Despite the fact that we're invoked for frames with SVG effects applied,
78 // we can actually get here. All continuations and special siblings of a
79 // frame with SVG effects applied will have the PreEffectsBBoxProperty
80 // property set on them. Therefore, the frames that are passed to us will
81 // always have that property set...well, with one exception. If the frames
82 // for an element with SVG effects applied have been subject to an "IB
83 // split", then the block frame(s) that caused the split will have been
84 // wrapped in anonymous, inline-block, nsBlockFrames of pseudo-type
85 // nsCSSAnonBoxes::mozAnonymousBlock. These "special sibling" anonymous
86 // blocks will have the PreEffectsBBoxProperty property set on them, but
87 // they will never be passed to us. Instead, we'll be passed the block
88 // children that they wrap, which don't have the PreEffectsBBoxProperty
89 // property set on them. This is actually okay. What we care about is
90 // collecting the _pre_ effects visual overflow rects of the frames to
91 // which the SVG effects have been applied. Since the IB split results in
92 // any overflow rect adjustments for transforms, effects, etc. taking
93 // place on the anonymous block wrappers, the wrapped children are left
94 // with their overflow rects unaffected. In other words, calling
95 // GetVisualOverflowRect() on the children will return their pre-effects
96 // visual overflow rects, just as we need.
98 // A couple of tests that demonstrate the IB split and cause us to get here
101 // * reftests/svg/svg-integration/clipPath-html-06.xhtml
102 // * reftests/svg/svg-integration/clipPath-html-06-extref.xhtml
104 // If we ever got passed a frame with the PreTransformOverflowAreasProperty
105 // property set, that would be bad, since then our GetVisualOverflowRect()
106 // call would give us the post-effects, and post-transform, overflow rect.
108 NS_ASSERTION(aFrame
->GetParent()->StyleContext()->GetPseudo() ==
109 nsCSSAnonBoxes::mozAnonymousBlock
,
110 "How did we getting here, then?");
111 NS_ASSERTION(!aFrame
->Properties().Get(
112 aFrame
->PreTransformOverflowAreasProperty()),
113 "GetVisualOverflowRect() won't return the pre-effects rect!");
114 return aFrame
->GetVisualOverflowRect();
117 nsIFrame
* mFirstContinuation
;
118 nsIFrame
* mCurrentFrame
;
119 const nsRect
& mCurrentFrameOverflowArea
;
124 * Gets the union of the pre-effects visual overflow rects of all of a frame's
125 * continuations, in "user space".
128 GetPreEffectsVisualOverflowUnion(nsIFrame
* aFirstContinuation
,
129 nsIFrame
* aCurrentFrame
,
130 const nsRect
& aCurrentFramePreEffectsOverflow
,
131 const nsPoint
& aFirstContinuationToUserSpace
)
133 NS_ASSERTION(!aFirstContinuation
->GetPrevContinuation(),
134 "Need first continuation here");
135 PreEffectsVisualOverflowCollector
collector(aFirstContinuation
,
137 aCurrentFramePreEffectsOverflow
);
138 // Compute union of all overflow areas relative to aFirstContinuation:
139 nsLayoutUtils::GetAllInFlowBoxes(aFirstContinuation
, &collector
);
140 // Return the result in user space:
141 return collector
.GetResult() + aFirstContinuationToUserSpace
;
146 nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame
* aFrame
)
148 // Even when SVG display lists are disabled, returning true for SVG frames
149 // does not adversely affect any of our callers. Therefore we don't bother
150 // checking the SDL prefs here, since we don't know if we're being called for
151 // painting or hit-testing anyway.
152 const nsStyleSVGReset
*style
= aFrame
->StyleSVGReset();
153 return (style
->mFilter
|| style
->mClipPath
|| style
->mMask
);
157 nsSVGIntegrationUtils::GetOffsetToUserSpace(nsIFrame
* aFrame
)
159 if ((aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
)) {
160 // Do NOT call GetAllInFlowRectsUnion for SVG - it will get the
161 // covered region relative to the nsSVGOuterSVGFrame, which is absolutely
162 // not what we want. SVG frames are always in user space, so they have
163 // no offset adjustment to make.
166 // We could allow aFrame to be any continuation, but since that would require
167 // a GetPrevContinuation() virtual call and conditional returns, and since
168 // all our current consumers always pass in the first continuation, we don't
170 NS_ASSERTION(!aFrame
->GetPrevContinuation(), "Not first continuation");
172 // The GetAllInFlowRectsUnion() call gets the union of the frame border-box
173 // rects over all continuations, relative to the origin (top-left of the
174 // border box) of its second argument (here, aFrame, the first continuation).
175 return -nsLayoutUtils::GetAllInFlowRectsUnion(aFrame
, aFrame
).TopLeft();
179 nsSVGIntegrationUtils::GetContinuationUnionSize(nsIFrame
* aNonSVGFrame
)
181 NS_ASSERTION(!aNonSVGFrame
->IsFrameOfType(nsIFrame::eSVG
),
182 "SVG frames should not get here");
183 nsIFrame
* firstFrame
=
184 nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aNonSVGFrame
);
185 return nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame
, firstFrame
).Size();
189 nsSVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(nsIFrame
* aNonSVGFrame
)
191 NS_ASSERTION(!aNonSVGFrame
->IsFrameOfType(nsIFrame::eSVG
),
192 "SVG frames should not get here");
193 nsIFrame
* firstFrame
=
194 nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aNonSVGFrame
);
195 nsRect r
= nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame
, firstFrame
);
196 nsPresContext
* presContext
= firstFrame
->PresContext();
197 return gfxSize(presContext
->AppUnitsToFloatCSSPixels(r
.width
),
198 presContext
->AppUnitsToFloatCSSPixels(r
.height
));
202 nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(nsIFrame
* aNonSVGFrame
)
204 NS_ASSERTION(!aNonSVGFrame
->IsFrameOfType(nsIFrame::eSVG
),
205 "SVG frames should not get here");
206 nsIFrame
* firstFrame
=
207 nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aNonSVGFrame
);
208 // 'r' is in "user space":
209 nsRect r
= GetPreEffectsVisualOverflowUnion(firstFrame
, nullptr, nsRect(),
210 GetOffsetToUserSpace(firstFrame
));
211 return nsLayoutUtils::RectToGfxRect(r
,
212 aNonSVGFrame
->PresContext()->AppUnitsPerCSSPixel());
215 // XXX Since we're called during reflow, this method is broken for frames with
216 // continuations. When we're called for a frame with continuations, we're
217 // called for each continuation in turn as it's reflowed. However, it isn't
218 // until the last continuation is reflowed that this method's
219 // GetOffsetToUserSpace() and GetPreEffectsVisualOverflowUnion() calls will
220 // obtain valid border boxes for all the continuations. As a result, we'll
221 // end up returning bogus post-filter visual overflow rects for all the prior
222 // continuations. Unfortunately, by the time the last continuation is
223 // reflowed, it's too late to go back and set and propagate the overflow
224 // rects on the previous continuations.
226 // The reason that we need to pass an override bbox to
227 // GetPreEffectsVisualOverflowUnion rather than just letting it call into our
228 // GetSVGBBoxForNonSVGFrame method is because we get called by
229 // ComputeOutlineAndEffectsRect when it has been called with
230 // aStoreRectProperties set to false. In this case the pre-effects visual
231 // overflow rect that it has been passed may be different to that stored on
232 // aFrame, resulting in a different bbox.
234 // XXXjwatt The pre-effects visual overflow rect passed to
235 // ComputeOutlineAndEffectsRect won't include continuation overflows, so
236 // for frames with continuation the following filter analysis will likely end
237 // up being carried out with a bbox created as if the frame didn't have
240 // XXXjwatt Using aPreEffectsOverflowRect to create the bbox isn't really right
241 // for SVG frames, since for SVG frames the SVG spec defines the bbox to be
242 // something quite different to the pre-effects visual overflow rect. However,
243 // we're essentially calculating an invalidation area here, and using the
244 // pre-effects overflow rect will actually overestimate that area which, while
245 // being a bit wasteful, isn't otherwise a problem.
248 nsSVGIntegrationUtils::
249 ComputePostEffectsVisualOverflowRect(nsIFrame
* aFrame
,
250 const nsRect
& aPreEffectsOverflowRect
)
252 NS_ASSERTION(!(aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
),
253 "Don't call this on SVG child frames");
255 nsIFrame
* firstFrame
=
256 nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame
);
257 nsSVGEffects::EffectProperties effectProperties
=
258 nsSVGEffects::GetEffectProperties(firstFrame
);
259 nsSVGFilterFrame
*filterFrame
= effectProperties
.mFilter
?
260 effectProperties
.mFilter
->GetFilterFrame() : nullptr;
262 return aPreEffectsOverflowRect
;
264 // Create an override bbox - see comment above:
265 nsPoint firstFrameToUserSpace
= GetOffsetToUserSpace(firstFrame
);
266 // overrideBBox is in "user space", in _CSS_ pixels:
267 // XXX Why are we rounding out to pixel boundaries? We don't do that in
268 // GetSVGBBoxForNonSVGFrame, and it doesn't appear to be necessary.
269 gfxRect overrideBBox
=
270 nsLayoutUtils::RectToGfxRect(
271 GetPreEffectsVisualOverflowUnion(firstFrame
, aFrame
,
272 aPreEffectsOverflowRect
,
273 firstFrameToUserSpace
),
274 aFrame
->PresContext()->AppUnitsPerCSSPixel());
275 overrideBBox
.RoundOut();
277 nsRect overflowRect
=
278 filterFrame
->GetPostFilterBounds(firstFrame
, &overrideBBox
);
280 // Return overflowRect relative to aFrame, rather than "user space":
281 return overflowRect
- (aFrame
->GetOffsetTo(firstFrame
) + firstFrameToUserSpace
);
285 nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame
* aFrame
,
286 const nsPoint
& aToReferenceFrame
,
287 const nsIntRect
& aInvalidRect
)
289 // Don't bother calling GetEffectProperties; the filter property should
290 // already have been set up during reflow/ComputeFrameEffectsRect
291 nsIFrame
* firstFrame
=
292 nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame
);
293 nsSVGEffects::EffectProperties effectProperties
=
294 nsSVGEffects::GetEffectProperties(firstFrame
);
295 if (!effectProperties
.mFilter
)
298 nsSVGFilterProperty
*prop
= nsSVGEffects::GetFilterProperty(firstFrame
);
299 if (!prop
|| !prop
->IsInObserverList()) {
303 int32_t appUnitsPerDevPixel
= aFrame
->PresContext()->AppUnitsPerDevPixel();
305 nsSVGFilterFrame
* filterFrame
= prop
->GetFilterFrame();
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 aInvalidRect into "user space" in app units:
316 nsPoint toUserSpace
=
317 aFrame
->GetOffsetTo(firstFrame
) + GetOffsetToUserSpace(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 toUserSpace
-= aToReferenceFrame
;
321 nsRect preEffectsRect
= aInvalidRect
.ToAppUnits(appUnitsPerDevPixel
) + toUserSpace
;
323 // Adjust the dirty area for effects, and shift it back to being relative to
324 // the reference frame.
325 nsRect result
= filterFrame
->GetPostFilterDirtyArea(firstFrame
, preEffectsRect
) -
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::GetFirstContinuationOrSpecialSibling(aFrame
);
339 nsSVGFilterFrame
* filterFrame
=
340 nsSVGEffects::GetFilterFrame(firstFrame
);
344 // Convert aDirtyRect into "user space" in app units:
345 nsPoint toUserSpace
=
346 aFrame
->GetOffsetTo(firstFrame
) + GetOffsetToUserSpace(firstFrame
);
347 nsRect postEffectsRect
= aDirtyRect
+ toUserSpace
;
349 // Return ther result, relative to aFrame, not in user space:
350 return filterFrame
->GetPreFilterNeededArea(firstFrame
, postEffectsRect
) -
355 nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame
* aFrame
, const nsPoint
& aPt
)
357 nsIFrame
* firstFrame
=
358 nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame
);
359 // Convert aPt to user space:
361 if (aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
) {
362 toUserSpace
= aFrame
->GetPosition();
365 aFrame
->GetOffsetTo(firstFrame
) + GetOffsetToUserSpace(firstFrame
);
367 nsPoint pt
= aPt
+ toUserSpace
;
368 return nsSVGUtils::HitTestClip(firstFrame
, pt
);
371 class RegularFramePaintCallback
: public nsSVGFilterPaintCallback
374 RegularFramePaintCallback(nsDisplayListBuilder
* aBuilder
,
375 LayerManager
* aManager
,
376 const nsPoint
& aOffset
)
377 : mBuilder(aBuilder
), mLayerManager(aManager
),
380 virtual void Paint(nsRenderingContext
*aContext
, nsIFrame
*aTarget
,
381 const nsIntRect
* aDirtyRect
)
383 BasicLayerManager
* basic
= static_cast<BasicLayerManager
*>(mLayerManager
);
384 basic
->SetTarget(aContext
->ThebesContext());
385 nsRenderingContext::AutoPushTranslation
push(aContext
, -mOffset
);
386 mLayerManager
->EndTransaction(FrameLayerBuilder::DrawThebesLayer
, mBuilder
);
390 nsDisplayListBuilder
* mBuilder
;
391 LayerManager
* mLayerManager
;
396 nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext
* aCtx
,
398 const nsRect
& aDirtyRect
,
399 nsDisplayListBuilder
* aBuilder
,
400 LayerManager
*aLayerManager
)
403 NS_ASSERTION(!(aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
) ||
404 (NS_SVGDisplayListPaintingEnabled() &&
405 !(aFrame
->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD
)),
406 "Should not use nsSVGIntegrationUtils on this SVG frame");
409 /* SVG defines the following rendering model:
413 * 3. Apply clipping, masking, group opacity
415 * We follow this, but perform a couple of optimizations:
417 * + Use cairo's clipPath when representable natively (single object
420 * + Merge opacity and masking if both used together.
423 const nsIContent
* content
= aFrame
->GetContent();
424 bool hasSVGLayout
= (aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
);
426 nsISVGChildFrame
*svgChildFrame
= do_QueryFrame(aFrame
);
427 if (!svgChildFrame
|| !aFrame
->GetContent()->IsSVG()) {
428 NS_ASSERTION(false, "why?");
431 if (!static_cast<const nsSVGElement
*>(content
)->HasValidDimensions()) {
432 return; // The SVG spec says not to draw _anything_
436 float opacity
= aFrame
->StyleDisplay()->mOpacity
;
437 if (opacity
== 0.0f
) {
440 if (opacity
!= 1.0f
&&
441 hasSVGLayout
&& nsSVGUtils::CanOptimizeOpacity(aFrame
)) {
445 /* Properties are added lazily and may have been removed by a restyle,
446 so make sure all applicable ones are set again. */
447 nsIFrame
* firstFrame
=
448 nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame
);
449 nsSVGEffects::EffectProperties effectProperties
=
450 nsSVGEffects::GetEffectProperties(firstFrame
);
453 nsSVGClipPathFrame
*clipPathFrame
= effectProperties
.GetClipPathFrame(&isOK
);
454 nsSVGFilterFrame
*filterFrame
= effectProperties
.GetFilterFrame(&isOK
);
455 nsSVGMaskFrame
*maskFrame
= effectProperties
.GetMaskFrame(&isOK
);
457 return; // Some resource is missing. We shouldn't paint anything.
460 bool isTrivialClip
= clipPathFrame
? clipPathFrame
->IsTrivial() : true;
462 gfxContext
* gfx
= aCtx
->ThebesContext();
463 gfxContextMatrixAutoSaveRestore
matrixAutoSaveRestore(gfx
);
465 nsPoint firstFrameOffset
= GetOffsetToUserSpace(firstFrame
);
466 nsPoint offset
= aBuilder
->ToReferenceFrame(firstFrame
) - firstFrameOffset
;
467 nsPoint offsetWithoutSVGGeomFramePos
= offset
;
468 nsPoint svgGeomFramePos
;
469 if (aFrame
->IsFrameOfType(nsIFrame::eSVGGeometry
) ||
470 aFrame
->IsSVGText()) {
471 // SVG leaf frames apply their offset themselves, we need to unapply it at
472 // various points below to prevent it being double counted.
473 svgGeomFramePos
= aFrame
->GetPosition();
474 offsetWithoutSVGGeomFramePos
-= svgGeomFramePos
;
477 aCtx
->Translate(offsetWithoutSVGGeomFramePos
);
479 gfxMatrix cssPxToDevPxMatrix
= GetCSSPxToDevPxMatrix(aFrame
);
481 bool complexEffects
= false;
482 /* Check if we need to do additional operations on this child's
483 * rendering, which necessitates rendering into another surface. */
484 if (opacity
!= 1.0f
|| maskFrame
|| (clipPathFrame
&& !isTrivialClip
)) {
485 complexEffects
= true;
487 aCtx
->IntersectClip(aFrame
->GetVisualOverflowRectRelativeToSelf() +
489 gfx
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
492 /* If this frame has only a trivial clipPath, set up cairo's clipping now so
493 * we can just do normal painting and get it clipped appropriately.
495 if (clipPathFrame
&& isTrivialClip
) {
497 clipPathFrame
->ClipPaint(aCtx
, aFrame
, cssPxToDevPxMatrix
);
500 /* Paint the child */
502 RegularFramePaintCallback
callback(aBuilder
, aLayerManager
,
503 offsetWithoutSVGGeomFramePos
);
504 nsRect dirtyRect
= aDirtyRect
- offset
;
505 filterFrame
->PaintFilteredFrame(aCtx
, aFrame
, &callback
, &dirtyRect
);
507 gfx
->SetMatrix(matrixAutoSaveRestore
.Matrix());
508 aLayerManager
->EndTransaction(FrameLayerBuilder::DrawThebesLayer
, aBuilder
);
509 aCtx
->Translate(offsetWithoutSVGGeomFramePos
);
512 if (clipPathFrame
&& isTrivialClip
) {
516 /* No more effects, we're done. */
517 if (!complexEffects
) {
521 gfx
->PopGroupToSource();
523 nsRefPtr
<gfxPattern
> maskSurface
=
524 maskFrame
? maskFrame
->ComputeMaskAlpha(aCtx
, aFrame
,
525 cssPxToDevPxMatrix
, opacity
) : nullptr;
527 nsRefPtr
<gfxPattern
> clipMaskSurface
;
528 if (clipPathFrame
&& !isTrivialClip
) {
529 gfx
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
531 nsresult rv
= clipPathFrame
->ClipPaint(aCtx
, aFrame
, cssPxToDevPxMatrix
);
532 clipMaskSurface
= gfx
->PopGroup();
534 if (NS_SUCCEEDED(rv
) && clipMaskSurface
) {
535 // Still more set after clipping, so clip to another surface
536 if (maskSurface
|| opacity
!= 1.0f
) {
537 gfx
->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA
);
538 gfx
->Mask(clipMaskSurface
);
539 gfx
->PopGroupToSource();
541 gfx
->Mask(clipMaskSurface
);
547 gfx
->Mask(maskSurface
);
548 } else if (opacity
!= 1.0f
) {
556 nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(nsIFrame
* aNonSVGFrame
)
558 int32_t appUnitsPerDevPixel
= aNonSVGFrame
->PresContext()->AppUnitsPerDevPixel();
559 float devPxPerCSSPx
=
560 1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel
);
562 return gfxMatrix(devPxPerCSSPx
, 0.0,
567 class PaintFrameCallback
: public gfxDrawingCallback
{
569 PaintFrameCallback(nsIFrame
* aFrame
,
570 const nsSize aPaintServerSize
,
571 const gfxIntSize aRenderSize
)
573 , mPaintServerSize(aPaintServerSize
)
574 , mRenderSize(aRenderSize
)
576 virtual bool operator()(gfxContext
* aContext
,
577 const gfxRect
& aFillRect
,
578 const gfxPattern::GraphicsFilter
& aFilter
,
579 const gfxMatrix
& aTransform
);
582 nsSize mPaintServerSize
;
583 gfxIntSize mRenderSize
;
587 PaintFrameCallback::operator()(gfxContext
* aContext
,
588 const gfxRect
& aFillRect
,
589 const gfxPattern::GraphicsFilter
& aFilter
,
590 const gfxMatrix
& aTransform
)
592 if (mFrame
->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER
)
595 mFrame
->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER
);
597 nsRenderingContext context
;
598 context
.Init(mFrame
->PresContext()->DeviceContext(), aContext
);
601 // Clip to aFillRect so that we don't paint outside.
603 aContext
->Rectangle(aFillRect
);
606 aContext
->Multiply(gfxMatrix(aTransform
).Invert());
608 // nsLayoutUtils::PaintFrame will anchor its painting at mFrame. But we want
609 // to have it anchored at the top left corner of the bounding box of all of
610 // mFrame's continuations. So we add a translation transform.
611 int32_t appUnitsPerDevPixel
= mFrame
->PresContext()->AppUnitsPerDevPixel();
612 nsPoint offset
= nsSVGIntegrationUtils::GetOffsetToUserSpace(mFrame
);
613 gfxPoint devPxOffset
= gfxPoint(offset
.x
, offset
.y
) / appUnitsPerDevPixel
;
614 aContext
->Multiply(gfxMatrix().Translate(devPxOffset
));
616 gfxSize paintServerSize
=
617 gfxSize(mPaintServerSize
.width
, mPaintServerSize
.height
) /
618 mFrame
->PresContext()->AppUnitsPerDevPixel();
620 // nsLayoutUtils::PaintFrame wants to render with paintServerSize, but we
621 // want it to render with mRenderSize, so we need to set up a scale transform.
622 gfxFloat scaleX
= mRenderSize
.width
/ paintServerSize
.width
;
623 gfxFloat scaleY
= mRenderSize
.height
/ paintServerSize
.height
;
624 gfxMatrix scaleMatrix
= gfxMatrix().Scale(scaleX
, scaleY
);
625 aContext
->Multiply(scaleMatrix
);
628 nsRect
dirty(-offset
.x
, -offset
.y
,
629 mPaintServerSize
.width
, mPaintServerSize
.height
);
630 nsLayoutUtils::PaintFrame(&context
, mFrame
,
631 dirty
, NS_RGBA(0, 0, 0, 0),
632 nsLayoutUtils::PAINT_IN_TRANSFORM
|
633 nsLayoutUtils::PAINT_ALL_CONTINUATIONS
);
637 mFrame
->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER
);
642 static already_AddRefed
<gfxDrawable
>
643 DrawableFromPaintServer(nsIFrame
* aFrame
,
645 const nsSize
& aPaintServerSize
,
646 const gfxIntSize
& aRenderSize
,
647 const gfxMatrix
& aContextMatrix
)
649 // aPaintServerSize is the size that would be filled when using
650 // background-repeat:no-repeat and background-size:auto. For normal background
651 // images, this would be the intrinsic size of the image; for gradients and
652 // patterns this would be the whole target frame fill area.
653 // aRenderSize is what we will be actually filling after accounting for
655 if (aFrame
->IsFrameOfType(nsIFrame::eSVGPaintServer
)) {
656 // aFrame is either a pattern or a gradient. These fill the whole target
657 // frame by default, so aPaintServerSize is the whole target background fill
659 nsSVGPaintServerFrame
* server
=
660 static_cast<nsSVGPaintServerFrame
*>(aFrame
);
662 gfxRect
overrideBounds(0, 0,
663 aPaintServerSize
.width
, aPaintServerSize
.height
);
664 overrideBounds
.ScaleInverse(aFrame
->PresContext()->AppUnitsPerDevPixel());
665 nsRefPtr
<gfxPattern
> pattern
=
666 server
->GetPaintServerPattern(aTarget
, aContextMatrix
,
667 &nsStyleSVG::mFill
, 1.0, &overrideBounds
);
672 // pattern is now set up to fill aPaintServerSize. But we want it to
673 // fill aRenderSize, so we need to add a scaling transform.
674 // We couldn't just have set overrideBounds to aRenderSize - it would have
675 // worked for gradients, but for patterns it would result in a different
677 gfxFloat scaleX
= overrideBounds
.Width() / aRenderSize
.width
;
678 gfxFloat scaleY
= overrideBounds
.Height() / aRenderSize
.height
;
679 gfxMatrix scaleMatrix
= gfxMatrix().Scale(scaleX
, scaleY
);
680 pattern
->SetMatrix(scaleMatrix
.Multiply(pattern
->GetMatrix()));
681 nsRefPtr
<gfxDrawable
> drawable
=
682 new gfxPatternDrawable(pattern
, aRenderSize
);
683 return drawable
.forget();
686 // We don't want to paint into a surface as long as we don't need to, so we
687 // set up a drawing callback.
688 nsRefPtr
<gfxDrawingCallback
> cb
=
689 new PaintFrameCallback(aFrame
, aPaintServerSize
, aRenderSize
);
690 nsRefPtr
<gfxDrawable
> drawable
= new gfxCallbackDrawable(cb
, aRenderSize
);
691 return drawable
.forget();
695 nsSVGIntegrationUtils::DrawPaintServer(nsRenderingContext
* aRenderingContext
,
697 nsIFrame
* aPaintServer
,
698 gfxPattern::GraphicsFilter aFilter
,
701 const nsPoint
& aAnchor
,
702 const nsRect
& aDirty
,
703 const nsSize
& aPaintServerSize
)
705 if (aDest
.IsEmpty() || aFill
.IsEmpty())
708 int32_t appUnitsPerDevPixel
= aTarget
->PresContext()->AppUnitsPerDevPixel();
709 nsRect destSize
= aDest
- aDest
.TopLeft();
710 nsIntSize roundedOut
= destSize
.ToOutsidePixels(appUnitsPerDevPixel
).Size();
711 gfxIntSize
imageSize(roundedOut
.width
, roundedOut
.height
);
712 nsRefPtr
<gfxDrawable
> drawable
=
713 DrawableFromPaintServer(aPaintServer
, aTarget
, aPaintServerSize
, imageSize
,
714 aRenderingContext
->ThebesContext()->CurrentMatrix());
717 nsLayoutUtils::DrawPixelSnapped(aRenderingContext
, drawable
, aFilter
,
718 aDest
, aFill
, aAnchor
, aDirty
);