Bumping manifests a=b2g-bump
[gecko.git] / layout / svg / nsSVGIntegrationUtils.cpp
blobae708b1c41b0980b74c88cbf53d0cc7f48ddc493
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/. */
6 // Main header first:
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 // ----------------------------------------------------------------------
32 /**
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
41 public:
42 /**
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 {
67 return mResult;
70 private:
72 static nsRect GetPreEffectsVisualOverflowRect(nsIFrame* aFrame) {
73 nsRect* r = static_cast<nsRect*>
74 (aFrame->Properties().Get(nsIFrame::PreEffectsBBoxProperty()));
75 if (r) {
76 return *r;
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
100 // are:
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;
121 nsRect mResult;
125 * Gets the union of the pre-effects visual overflow rects of all of a frame's
126 * continuations, in "user space".
128 static nsRect
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,
137 aCurrentFrame,
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;
146 bool
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.
159 static nsPoint
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.
167 return nsPoint();
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
172 // currently bother.
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();
181 /* static */ nsSize
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));
204 gfxRect
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
241 // continuations.
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.
250 nsRect
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);
286 nsIntRegion
287 nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame,
288 const nsPoint& aToReferenceFrame,
289 const nsIntRegion& aInvalidRegion)
291 if (aInvalidRegion.IsEmpty()) {
292 return nsIntRect();
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);
331 nsRect
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()) {
341 return aDirtyRect;
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()
351 - toUserSpace;
354 bool
355 nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt)
357 nsIFrame* firstFrame =
358 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
359 // Convert aPt to user space:
360 nsPoint toUserSpace;
361 if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
362 // XXXmstange Isn't this wrong for svg:use and innerSVG frames?
363 toUserSpace = aFrame->GetPosition();
364 } else {
365 toUserSpace =
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
376 public:
377 RegularFramePaintCallback(nsDisplayListBuilder* aBuilder,
378 LayerManager* aManager,
379 const nsPoint& aOffset)
380 : mBuilder(aBuilder), mLayerManager(aManager),
381 mOffset(aOffset) {}
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);
393 private:
394 nsDisplayListBuilder* mBuilder;
395 LayerManager* mLayerManager;
396 nsPoint mOffset;
399 void
400 nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx,
401 nsIFrame* aFrame,
402 const nsRect& aDirtyRect,
403 nsDisplayListBuilder* aBuilder,
404 LayerManager *aLayerManager)
406 #ifdef DEBUG
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");
411 #endif
413 /* SVG defines the following rendering model:
415 * 1. Render geometry
416 * 2. Apply filter
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
422 * clip region).
424 * + Merge opacity and masking if both used together.
427 const nsIContent* content = aFrame->GetContent();
428 bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
429 if (hasSVGLayout) {
430 nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame);
431 if (!svgChildFrame || !aFrame->GetContent()->IsSVG()) {
432 NS_ASSERTION(false, "why?");
433 return;
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) {
442 return;
444 if (opacity != 1.0f &&
445 hasSVGLayout && nsSVGUtils::CanOptimizeOpacity(aFrame)) {
446 opacity = 1.0f;
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);
459 if (!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;
507 gfx->Save();
508 aCtx->IntersectClip(aFrame->GetVisualOverflowRectRelativeToSelf() +
509 toUserSpace);
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) {
517 gfx->Save();
518 clipPathFrame->ApplyClipOrPaintClipMask(aCtx, aFrame, cssPxToDevPxMatrix);
521 /* Paint the child */
522 if (effectProperties.HasValidFilter()) {
523 RegularFramePaintCallback callback(aBuilder, aLayerManager,
524 offsetToUserSpace);
526 nsRegion dirtyRegion = aDirtyRect - offsetToBoundingBox;
527 nsFilterInstance::PaintFilteredFrame(aCtx, aFrame, &callback, &dirtyRegion);
528 } else {
529 gfx->SetMatrix(matrixAutoSaveRestore.Matrix());
530 aLayerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder);
531 aCtx->Translate(offsetToUserSpace);
534 if (clipPathFrame && isTrivialClip) {
535 gfx->Restore();
538 /* No more effects, we're done. */
539 if (!complexEffects) {
540 return;
543 gfx->PopGroupToSource();
545 nsRefPtr<gfxPattern> maskSurface =
546 maskFrame ? maskFrame->GetMaskForMaskedFrame(aCtx->ThebesContext(),
547 aFrame, cssPxToDevPxMatrix,
548 opacity)
549 : nullptr;
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();
564 } else {
565 gfx->Mask(clipMaskSurface);
570 if (maskSurface) {
571 gfx->Mask(maskSurface);
572 } else if (opacity != 1.0f) {
573 gfx->Paint(opacity);
576 gfx->Restore();
579 gfxMatrix
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,
587 0.0, devPxPerCSSPx,
588 0.0, 0.0);
591 class PaintFrameCallback : public gfxDrawingCallback {
592 public:
593 PaintFrameCallback(nsIFrame* aFrame,
594 const nsSize aPaintServerSize,
595 const gfxIntSize aRenderSize,
596 uint32_t aFlags)
597 : mFrame(aFrame)
598 , mPaintServerSize(aPaintServerSize)
599 , mRenderSize(aRenderSize)
600 , mFlags (aFlags)
602 virtual bool operator()(gfxContext* aContext,
603 const gfxRect& aFillRect,
604 const GraphicsFilter& aFilter,
605 const gfxMatrix& aTransform) MOZ_OVERRIDE;
606 private:
607 nsIFrame* mFrame;
608 nsSize mPaintServerSize;
609 gfxIntSize mRenderSize;
610 uint32_t mFlags;
613 bool
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)
620 return false;
622 mFrame->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
624 nsRefPtr<nsRenderingContext> context(new nsRenderingContext());
625 context->Init(mFrame->PresContext()->DeviceContext(), aContext);
626 aContext->Save();
628 // Clip to aFillRect so that we don't paint outside.
629 aContext->NewPath();
630 aContext->Rectangle(aFillRect);
631 aContext->Clip();
633 gfxMatrix invmatrix = aTransform;
634 if (!invmatrix.Invert()) {
635 return false;
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);
658 // Draw.
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),
669 flags);
671 aContext->Restore();
673 mFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
675 return true;
678 /* static */ already_AddRefed<gfxDrawable>
679 nsSVGIntegrationUtils::DrawableFromPaintServer(nsIFrame* aFrame,
680 nsIFrame* aTarget,
681 const nsSize& aPaintServerSize,
682 const gfxIntSize& aRenderSize,
683 const gfxMatrix& aContextMatrix,
684 uint32_t aFlags)
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
691 // background-size.
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
695 // area.
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);
706 if (!pattern)
707 return nullptr;
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
713 // pattern size.
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();