Bug 867089 - Validate the playbackRate before using it. r=ehsan
[gecko.git] / layout / svg / nsSVGIntegrationUtils.cpp
blob8ad7527f14332c50598f490437991eb26fe27c4c
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 "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 // ----------------------------------------------------------------------
31 /**
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
40 public:
41 /**
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 {
66 return mResult;
69 private:
71 static nsRect GetPreEffectsVisualOverflowRect(nsIFrame* aFrame) {
72 nsRect* r = static_cast<nsRect*>
73 (aFrame->Properties().Get(nsIFrame::PreEffectsBBoxProperty()));
74 if (r) {
75 return *r;
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
99 // are:
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;
120 nsRect mResult;
124 * Gets the union of the pre-effects visual overflow rects of all of a frame's
125 * continuations, in "user space".
127 static nsRect
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,
136 aCurrentFrame,
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;
145 bool
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);
156 /* static */ nsPoint
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.
164 return nsPoint();
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
169 // currently bother.
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();
178 /* static */ nsSize
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();
188 /* static */ gfxSize
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));
201 gfxRect
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
238 // continuations.
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.
247 nsRect
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;
261 if (!filterFrame)
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);
284 nsIntRect
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)
296 return aInvalidRect;
298 nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame);
299 if (!prop || !prop->IsInObserverList()) {
300 return aInvalidRect;
303 int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
305 nsSVGFilterFrame* filterFrame = prop->GetFilterFrame();
306 if (!filterFrame) {
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) -
326 toUserSpace;
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::GetFirstContinuationOrSpecialSibling(aFrame);
339 nsSVGFilterFrame* filterFrame =
340 nsSVGEffects::GetFilterFrame(firstFrame);
341 if (!filterFrame)
342 return aDirtyRect;
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) -
351 toUserSpace;
354 bool
355 nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt)
357 nsIFrame* firstFrame =
358 nsLayoutUtils::GetFirstContinuationOrSpecialSibling(aFrame);
359 // Convert aPt to user space:
360 nsPoint toUserSpace;
361 if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
362 toUserSpace = aFrame->GetPosition();
363 } else {
364 toUserSpace =
365 aFrame->GetOffsetTo(firstFrame) + GetOffsetToUserSpace(firstFrame);
367 nsPoint pt = aPt + toUserSpace;
368 return nsSVGUtils::HitTestClip(firstFrame, pt);
371 class RegularFramePaintCallback : public nsSVGFilterPaintCallback
373 public:
374 RegularFramePaintCallback(nsDisplayListBuilder* aBuilder,
375 LayerManager* aManager,
376 const nsPoint& aOffset)
377 : mBuilder(aBuilder), mLayerManager(aManager),
378 mOffset(aOffset) {}
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);
389 private:
390 nsDisplayListBuilder* mBuilder;
391 LayerManager* mLayerManager;
392 nsPoint mOffset;
395 void
396 nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx,
397 nsIFrame* aFrame,
398 const nsRect& aDirtyRect,
399 nsDisplayListBuilder* aBuilder,
400 LayerManager *aLayerManager)
402 #ifdef DEBUG
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");
407 #endif
409 /* SVG defines the following rendering model:
411 * 1. Render geometry
412 * 2. Apply filter
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
418 * clip region).
420 * + Merge opacity and masking if both used together.
423 const nsIContent* content = aFrame->GetContent();
424 bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
425 if (hasSVGLayout) {
426 nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame);
427 if (!svgChildFrame || !aFrame->GetContent()->IsSVG()) {
428 NS_ASSERTION(false, "why?");
429 return;
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) {
438 return;
440 if (opacity != 1.0f &&
441 hasSVGLayout && nsSVGUtils::CanOptimizeOpacity(aFrame)) {
442 opacity = 1.0f;
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);
452 bool isOK = true;
453 nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
454 nsSVGFilterFrame *filterFrame = effectProperties.GetFilterFrame(&isOK);
455 nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
456 if (!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;
486 gfx->Save();
487 aCtx->IntersectClip(aFrame->GetVisualOverflowRectRelativeToSelf() +
488 svgGeomFramePos);
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) {
496 gfx->Save();
497 clipPathFrame->ClipPaint(aCtx, aFrame, cssPxToDevPxMatrix);
500 /* Paint the child */
501 if (filterFrame) {
502 RegularFramePaintCallback callback(aBuilder, aLayerManager,
503 offsetWithoutSVGGeomFramePos);
504 nsRect dirtyRect = aDirtyRect - offset;
505 filterFrame->PaintFilteredFrame(aCtx, aFrame, &callback, &dirtyRect);
506 } else {
507 gfx->SetMatrix(matrixAutoSaveRestore.Matrix());
508 aLayerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder);
509 aCtx->Translate(offsetWithoutSVGGeomFramePos);
512 if (clipPathFrame && isTrivialClip) {
513 gfx->Restore();
516 /* No more effects, we're done. */
517 if (!complexEffects) {
518 return;
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();
540 } else {
541 gfx->Mask(clipMaskSurface);
546 if (maskSurface) {
547 gfx->Mask(maskSurface);
548 } else if (opacity != 1.0f) {
549 gfx->Paint(opacity);
552 gfx->Restore();
555 gfxMatrix
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,
563 0.0, devPxPerCSSPx,
564 0.0, 0.0);
567 class PaintFrameCallback : public gfxDrawingCallback {
568 public:
569 PaintFrameCallback(nsIFrame* aFrame,
570 const nsSize aPaintServerSize,
571 const gfxIntSize aRenderSize)
572 : mFrame(aFrame)
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);
580 private:
581 nsIFrame* mFrame;
582 nsSize mPaintServerSize;
583 gfxIntSize mRenderSize;
586 bool
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)
593 return false;
595 mFrame->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
597 nsRenderingContext context;
598 context.Init(mFrame->PresContext()->DeviceContext(), aContext);
599 aContext->Save();
601 // Clip to aFillRect so that we don't paint outside.
602 aContext->NewPath();
603 aContext->Rectangle(aFillRect);
604 aContext->Clip();
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);
627 // Draw.
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);
635 aContext->Restore();
637 mFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
639 return true;
642 static already_AddRefed<gfxDrawable>
643 DrawableFromPaintServer(nsIFrame* aFrame,
644 nsIFrame* aTarget,
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
654 // background-size.
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
658 // area.
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);
669 if (!pattern)
670 return nullptr;
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
676 // pattern size.
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();
694 /* static */ void
695 nsSVGIntegrationUtils::DrawPaintServer(nsRenderingContext* aRenderingContext,
696 nsIFrame* aTarget,
697 nsIFrame* aPaintServer,
698 gfxPattern::GraphicsFilter aFilter,
699 const nsRect& aDest,
700 const nsRect& aFill,
701 const nsPoint& aAnchor,
702 const nsRect& aDirty,
703 const nsSize& aPaintServerSize)
705 if (aDest.IsEmpty() || aFill.IsEmpty())
706 return;
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());
716 if (drawable) {
717 nsLayoutUtils::DrawPixelSnapped(aRenderingContext, drawable, aFilter,
718 aDest, aFill, aAnchor, aDirty);