layout/svg/nsSVGIntegrationUtils.cpp

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:5fc54ab750cd
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 // Main header first:
7 #include "nsSVGIntegrationUtils.h"
8
9 // Keep others in (case-insensitive) order:
10 #include "gfxDrawable.h"
11 #include "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"
26
27 using namespace mozilla;
28 using namespace mozilla::layers;
29
30 // ----------------------------------------------------------------------
31
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.
38 */
39 class PreEffectsVisualOverflowCollector : public nsLayoutUtils::BoxCallback
40 {
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.
48 */
49 PreEffectsVisualOverflowCollector(nsIFrame* aFirstContinuation,
50 nsIFrame* aCurrentFrame,
51 const nsRect& aCurrentFrameOverflowArea)
52 : mFirstContinuation(aFirstContinuation)
53 , mCurrentFrame(aCurrentFrame)
54 , mCurrentFrameOverflowArea(aCurrentFrameOverflowArea)
55 {
56 NS_ASSERTION(!mFirstContinuation->GetPrevContinuation(),
57 "We want the first continuation here");
58 }
59
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));
64 }
65
66 nsRect GetResult() const {
67 return mResult;
68 }
69
70 private:
71
72 static nsRect GetPreEffectsVisualOverflowRect(nsIFrame* aFrame) {
73 nsRect* r = static_cast<nsRect*>
74 (aFrame->Properties().Get(nsIFrame::PreEffectsBBoxProperty()));
75 if (r) {
76 return *r;
77 }
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.
98 //
99 // A couple of tests that demonstrate the IB split and cause us to get here
100 // are:
101 //
102 // * reftests/svg/svg-integration/clipPath-html-06.xhtml
103 // * reftests/svg/svg-integration/clipPath-html-06-extref.xhtml
104 //
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.
108 //
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();
116 }
117
118 nsIFrame* mFirstContinuation;
119 nsIFrame* mCurrentFrame;
120 const nsRect& mCurrentFrameOverflowArea;
121 nsRect mResult;
122 };
123
124 /**
125 * Gets the union of the pre-effects visual overflow rects of all of a frame's
126 * continuations, in "user space".
127 */
128 static nsRect
129 GetPreEffectsVisualOverflowUnion(nsIFrame* aFirstContinuation,
130 nsIFrame* aCurrentFrame,
131 const nsRect& aCurrentFramePreEffectsOverflow,
132 const nsPoint& aFirstContinuationToUserSpace)
133 {
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;
143 }
144
145
146 bool
147 nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame)
148 {
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);
155 }
156
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)
161 {
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();
168 }
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");
174
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();
179 }
180
181 /* static */ nsSize
182 nsSVGIntegrationUtils::GetContinuationUnionSize(nsIFrame* aNonSVGFrame)
183 {
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();
189 }
190
191 /* static */ gfx::Size
192 nsSVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(nsIFrame* aNonSVGFrame)
193 {
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));
202 }
203
204 gfxRect
205 nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame)
206 {
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());
216 }
217
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.
228 //
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.
236 //
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.
242 //
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.
249 //
250 nsRect
251 nsSVGIntegrationUtils::
252 ComputePostEffectsVisualOverflowRect(nsIFrame* aFrame,
253 const nsRect& aPreEffectsOverflowRect)
254 {
255 NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT),
256 "Don't call this on SVG child frames");
257
258 nsIFrame* firstFrame =
259 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
260 nsSVGEffects::EffectProperties effectProperties =
261 nsSVGEffects::GetEffectProperties(firstFrame);
262 if (!effectProperties.HasValidFilter()) {
263 return aPreEffectsOverflowRect;
264 }
265
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();
278
279 nsRect overflowRect =
280 nsFilterInstance::GetPostFilterBounds(firstFrame, &overrideBBox);
281
282 // Return overflowRect relative to aFrame, rather than "user space":
283 return overflowRect - (aFrame->GetOffsetTo(firstFrame) + firstFrameToBoundingBox);
284 }
285
286 nsIntRegion
287 nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame,
288 const nsPoint& aToReferenceFrame,
289 const nsIntRegion& aInvalidRegion)
290 {
291 if (aInvalidRegion.IsEmpty()) {
292 return nsIntRect();
293 }
294
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;
302 }
303
304 int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
305
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);
313 }
314
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);
322
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);
329 }
330
331 nsRect
332 nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
333 const nsRect& aDirtyRect)
334 {
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;
342 }
343
344 // Convert aDirtyRect into "user space" in app units:
345 nsPoint toUserSpace =
346 aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame);
347 nsRect postEffectsRect = aDirtyRect + toUserSpace;
348
349 // Return ther result, relative to aFrame, not in user space:
350 return nsFilterInstance::GetPreFilterNeededArea(firstFrame, postEffectsRect).GetBounds()
351 - toUserSpace;
352 }
353
354 bool
355 nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt)
356 {
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);
367 }
368 nsPoint pt = aPt + toUserSpace;
369 return nsSVGUtils::HitTestClip(firstFrame, pt);
370 }
371
372 class RegularFramePaintCallback : public nsSVGFilterPaintCallback
373 {
374 public:
375 RegularFramePaintCallback(nsDisplayListBuilder* aBuilder,
376 LayerManager* aManager,
377 const nsPoint& aOffset)
378 : mBuilder(aBuilder), mLayerManager(aManager),
379 mOffset(aOffset) {}
380
381 virtual void Paint(nsRenderingContext *aContext, nsIFrame *aTarget,
382 const nsIntRect* aDirtyRect,
383 nsIFrame* aTransformRoot) MOZ_OVERRIDE
384 {
385 BasicLayerManager* basic = static_cast<BasicLayerManager*>(mLayerManager);
386 basic->SetTarget(aContext->ThebesContext());
387 nsRenderingContext::AutoPushTranslation push(aContext, -mOffset);
388 mLayerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, mBuilder);
389 }
390
391 private:
392 nsDisplayListBuilder* mBuilder;
393 LayerManager* mLayerManager;
394 nsPoint mOffset;
395 };
396
397 void
398 nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx,
399 nsIFrame* aFrame,
400 const nsRect& aDirtyRect,
401 nsDisplayListBuilder* aBuilder,
402 LayerManager *aLayerManager)
403 {
404 #ifdef DEBUG
405 NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
406 (NS_SVGDisplayListPaintingEnabled() &&
407 !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)),
408 "Should not use nsSVGIntegrationUtils on this SVG frame");
409 #endif
410
411 /* SVG defines the following rendering model:
412 *
413 * 1. Render geometry
414 * 2. Apply filter
415 * 3. Apply clipping, masking, group opacity
416 *
417 * We follow this, but perform a couple of optimizations:
418 *
419 * + Use cairo's clipPath when representable natively (single object
420 * clip region).
421 *
422 * + Merge opacity and masking if both used together.
423 */
424
425 const nsIContent* content = aFrame->GetContent();
426 bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
427 if (hasSVGLayout) {
428 nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame);
429 if (!svgChildFrame || !aFrame->GetContent()->IsSVG()) {
430 NS_ASSERTION(false, "why?");
431 return;
432 }
433 if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
434 return; // The SVG spec says not to draw _anything_
435 }
436 }
437
438 float opacity = aFrame->StyleDisplay()->mOpacity;
439 if (opacity == 0.0f) {
440 return;
441 }
442 if (opacity != 1.0f &&
443 hasSVGLayout && nsSVGUtils::CanOptimizeOpacity(aFrame)) {
444 opacity = 1.0f;
445 }
446
447 /* Properties are added lazily and may have been removed by a restyle,
448 so make sure all applicable ones are set again. */
449 nsIFrame* firstFrame =
450 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
451 nsSVGEffects::EffectProperties effectProperties =
452 nsSVGEffects::GetEffectProperties(firstFrame);
453
454 bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
455 nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
456 nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
457 if (!isOK) {
458 return; // Some resource is missing. We shouldn't paint anything.
459 }
460
461 bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;
462
463 gfxContext* gfx = aCtx->ThebesContext();
464 gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(gfx);
465
466 nsPoint firstFrameOffset = GetOffsetToBoundingBox(firstFrame);
467 nsPoint offsetToBoundingBox = aBuilder->ToReferenceFrame(firstFrame) - firstFrameOffset;
468 if (!firstFrame->IsFrameOfType(nsIFrame::eSVG)) {
469 /* Snap the offset if the reference frame is not a SVG frame,
470 * since other frames will be snapped to pixel when rendering. */
471 offsetToBoundingBox = nsPoint(
472 aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.x),
473 aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.y));
474 }
475
476 // After applying only "offsetToBoundingBox", aCtx would have its origin at
477 // the top left corner of aFrame's bounding box (over all continuations).
478 // However, SVG painting needs the origin to be located at the origin of the
479 // SVG frame's "user space", i.e. the space in which, for example, the
480 // frame's BBox lives.
481 // SVG geometry frames and foreignObject frames apply their own offsets, so
482 // their position is relative to their user space. So for these frame types,
483 // if we want aCtx to be in user space, we first need to subtract the
484 // frame's position so that SVG painting can later add it again and the
485 // frame is painted in the right place.
486
487 gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);
488 nsPoint toUserSpace(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
489 nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
490 nsPoint offsetToUserSpace = offsetToBoundingBox - toUserSpace;
491
492 NS_ASSERTION(hasSVGLayout || offsetToBoundingBox == offsetToUserSpace,
493 "For non-SVG frames there shouldn't be any additional offset");
494
495 aCtx->Translate(offsetToUserSpace);
496
497 gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(aFrame);
498
499 bool complexEffects = false;
500 /* Check if we need to do additional operations on this child's
501 * rendering, which necessitates rendering into another surface. */
502 if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)
503 || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
504 complexEffects = true;
505 gfx->Save();
506 aCtx->IntersectClip(aFrame->GetVisualOverflowRectRelativeToSelf() +
507 toUserSpace);
508 gfx->PushGroup(gfxContentType::COLOR_ALPHA);
509 }
510
511 /* If this frame has only a trivial clipPath, set up cairo's clipping now so
512 * we can just do normal painting and get it clipped appropriately.
513 */
514 if (clipPathFrame && isTrivialClip) {
515 gfx->Save();
516 clipPathFrame->ClipPaint(aCtx, aFrame, cssPxToDevPxMatrix);
517 }
518
519 /* Paint the child */
520 if (effectProperties.HasValidFilter()) {
521 RegularFramePaintCallback callback(aBuilder, aLayerManager,
522 offsetToUserSpace);
523
524 nsRegion dirtyRegion = aDirtyRect - offsetToBoundingBox;
525 nsFilterInstance::PaintFilteredFrame(aCtx, aFrame, &callback, &dirtyRegion);
526 } else {
527 gfx->SetMatrix(matrixAutoSaveRestore.Matrix());
528 aLayerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder);
529 aCtx->Translate(offsetToUserSpace);
530 }
531
532 if (clipPathFrame && isTrivialClip) {
533 gfx->Restore();
534 }
535
536 /* No more effects, we're done. */
537 if (!complexEffects) {
538 return;
539 }
540
541 gfx->PopGroupToSource();
542
543 nsRefPtr<gfxPattern> maskSurface =
544 maskFrame ? maskFrame->ComputeMaskAlpha(aCtx, aFrame,
545 cssPxToDevPxMatrix, opacity) : nullptr;
546
547 nsRefPtr<gfxPattern> clipMaskSurface;
548 if (clipPathFrame && !isTrivialClip) {
549 gfx->PushGroup(gfxContentType::COLOR_ALPHA);
550
551 nsresult rv = clipPathFrame->ClipPaint(aCtx, aFrame, cssPxToDevPxMatrix);
552 clipMaskSurface = gfx->PopGroup();
553
554 if (NS_SUCCEEDED(rv) && clipMaskSurface) {
555 // Still more set after clipping, so clip to another surface
556 if (maskSurface || opacity != 1.0f) {
557 gfx->PushGroup(gfxContentType::COLOR_ALPHA);
558 gfx->Mask(clipMaskSurface);
559 gfx->PopGroupToSource();
560 } else {
561 gfx->Mask(clipMaskSurface);
562 }
563 }
564 }
565
566 if (maskSurface) {
567 gfx->Mask(maskSurface);
568 } else if (opacity != 1.0f) {
569 gfx->Paint(opacity);
570 }
571
572 gfx->Restore();
573 }
574
575 gfxMatrix
576 nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame)
577 {
578 int32_t appUnitsPerDevPixel = aNonSVGFrame->PresContext()->AppUnitsPerDevPixel();
579 float devPxPerCSSPx =
580 1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
581
582 return gfxMatrix(devPxPerCSSPx, 0.0,
583 0.0, devPxPerCSSPx,
584 0.0, 0.0);
585 }
586
587 class PaintFrameCallback : public gfxDrawingCallback {
588 public:
589 PaintFrameCallback(nsIFrame* aFrame,
590 const nsSize aPaintServerSize,
591 const gfxIntSize aRenderSize,
592 uint32_t aFlags)
593 : mFrame(aFrame)
594 , mPaintServerSize(aPaintServerSize)
595 , mRenderSize(aRenderSize)
596 , mFlags (aFlags)
597 {}
598 virtual bool operator()(gfxContext* aContext,
599 const gfxRect& aFillRect,
600 const GraphicsFilter& aFilter,
601 const gfxMatrix& aTransform) MOZ_OVERRIDE;
602 private:
603 nsIFrame* mFrame;
604 nsSize mPaintServerSize;
605 gfxIntSize mRenderSize;
606 uint32_t mFlags;
607 };
608
609 bool
610 PaintFrameCallback::operator()(gfxContext* aContext,
611 const gfxRect& aFillRect,
612 const GraphicsFilter& aFilter,
613 const gfxMatrix& aTransform)
614 {
615 if (mFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)
616 return false;
617
618 mFrame->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
619
620 nsRefPtr<nsRenderingContext> context(new nsRenderingContext());
621 context->Init(mFrame->PresContext()->DeviceContext(), aContext);
622 aContext->Save();
623
624 // Clip to aFillRect so that we don't paint outside.
625 aContext->NewPath();
626 aContext->Rectangle(aFillRect);
627 aContext->Clip();
628
629 aContext->Multiply(gfxMatrix(aTransform).Invert());
630
631 // nsLayoutUtils::PaintFrame will anchor its painting at mFrame. But we want
632 // to have it anchored at the top left corner of the bounding box of all of
633 // mFrame's continuations. So we add a translation transform.
634 int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
635 nsPoint offset = GetOffsetToBoundingBox(mFrame);
636 gfxPoint devPxOffset = gfxPoint(offset.x, offset.y) / appUnitsPerDevPixel;
637 aContext->Multiply(gfxMatrix().Translate(devPxOffset));
638
639 gfxSize paintServerSize =
640 gfxSize(mPaintServerSize.width, mPaintServerSize.height) /
641 mFrame->PresContext()->AppUnitsPerDevPixel();
642
643 // nsLayoutUtils::PaintFrame wants to render with paintServerSize, but we
644 // want it to render with mRenderSize, so we need to set up a scale transform.
645 gfxFloat scaleX = mRenderSize.width / paintServerSize.width;
646 gfxFloat scaleY = mRenderSize.height / paintServerSize.height;
647 gfxMatrix scaleMatrix = gfxMatrix().Scale(scaleX, scaleY);
648 aContext->Multiply(scaleMatrix);
649
650 // Draw.
651 nsRect dirty(-offset.x, -offset.y,
652 mPaintServerSize.width, mPaintServerSize.height);
653
654 uint32_t flags = nsLayoutUtils::PAINT_IN_TRANSFORM |
655 nsLayoutUtils::PAINT_ALL_CONTINUATIONS;
656 if (mFlags & nsSVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES) {
657 flags |= nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES;
658 }
659 nsLayoutUtils::PaintFrame(context, mFrame,
660 dirty, NS_RGBA(0, 0, 0, 0),
661 flags);
662
663 aContext->Restore();
664
665 mFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
666
667 return true;
668 }
669
670 /* static */ already_AddRefed<gfxDrawable>
671 nsSVGIntegrationUtils::DrawableFromPaintServer(nsIFrame* aFrame,
672 nsIFrame* aTarget,
673 const nsSize& aPaintServerSize,
674 const gfxIntSize& aRenderSize,
675 const gfxMatrix& aContextMatrix,
676 uint32_t aFlags)
677 {
678 // aPaintServerSize is the size that would be filled when using
679 // background-repeat:no-repeat and background-size:auto. For normal background
680 // images, this would be the intrinsic size of the image; for gradients and
681 // patterns this would be the whole target frame fill area.
682 // aRenderSize is what we will be actually filling after accounting for
683 // background-size.
684 if (aFrame->IsFrameOfType(nsIFrame::eSVGPaintServer)) {
685 // aFrame is either a pattern or a gradient. These fill the whole target
686 // frame by default, so aPaintServerSize is the whole target background fill
687 // area.
688 nsSVGPaintServerFrame* server =
689 static_cast<nsSVGPaintServerFrame*>(aFrame);
690
691 gfxRect overrideBounds(0, 0,
692 aPaintServerSize.width, aPaintServerSize.height);
693 overrideBounds.ScaleInverse(aFrame->PresContext()->AppUnitsPerDevPixel());
694 nsRefPtr<gfxPattern> pattern =
695 server->GetPaintServerPattern(aTarget, aContextMatrix,
696 &nsStyleSVG::mFill, 1.0, &overrideBounds);
697
698 if (!pattern)
699 return nullptr;
700
701 // pattern is now set up to fill aPaintServerSize. But we want it to
702 // fill aRenderSize, so we need to add a scaling transform.
703 // We couldn't just have set overrideBounds to aRenderSize - it would have
704 // worked for gradients, but for patterns it would result in a different
705 // pattern size.
706 gfxFloat scaleX = overrideBounds.Width() / aRenderSize.width;
707 gfxFloat scaleY = overrideBounds.Height() / aRenderSize.height;
708 gfxMatrix scaleMatrix = gfxMatrix().Scale(scaleX, scaleY);
709 pattern->SetMatrix(scaleMatrix.Multiply(pattern->GetMatrix()));
710 nsRefPtr<gfxDrawable> drawable =
711 new gfxPatternDrawable(pattern, aRenderSize);
712 return drawable.forget();
713 }
714
715 // We don't want to paint into a surface as long as we don't need to, so we
716 // set up a drawing callback.
717 nsRefPtr<gfxDrawingCallback> cb =
718 new PaintFrameCallback(aFrame, aPaintServerSize, aRenderSize, aFlags);
719 nsRefPtr<gfxDrawable> drawable = new gfxCallbackDrawable(cb, aRenderSize);
720 return drawable.forget();
721 }

mercurial