/src/mozilla-central/layout/svg/nsSVGIntegrationUtils.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | // Main header first: |
8 | | #include "nsSVGIntegrationUtils.h" |
9 | | |
10 | | // Keep others in (case-insensitive) order: |
11 | | #include "gfxDrawable.h" |
12 | | #include "gfxPrefs.h" |
13 | | #include "nsCSSAnonBoxes.h" |
14 | | #include "nsCSSClipPathInstance.h" |
15 | | #include "nsDisplayList.h" |
16 | | #include "nsFilterInstance.h" |
17 | | #include "nsLayoutUtils.h" |
18 | | #include "gfxContext.h" |
19 | | #include "nsSVGClipPathFrame.h" |
20 | | #include "SVGObserverUtils.h" |
21 | | #include "nsSVGElement.h" |
22 | | #include "nsSVGFilterPaintCallback.h" |
23 | | #include "nsSVGMaskFrame.h" |
24 | | #include "nsSVGPaintServerFrame.h" |
25 | | #include "nsSVGUtils.h" |
26 | | #include "FrameLayerBuilder.h" |
27 | | #include "BasicLayers.h" |
28 | | #include "mozilla/gfx/Point.h" |
29 | | #include "nsCSSRendering.h" |
30 | | #include "mozilla/Unused.h" |
31 | | |
32 | | using namespace mozilla; |
33 | | using namespace mozilla::layers; |
34 | | using namespace mozilla::gfx; |
35 | | using namespace mozilla::image; |
36 | | |
37 | | // ---------------------------------------------------------------------- |
38 | | |
39 | | /** |
40 | | * This class is used to get the pre-effects visual overflow rect of a frame, |
41 | | * or, in the case of a frame with continuations, to collect the union of the |
42 | | * pre-effects visual overflow rects of all the continuations. The result is |
43 | | * relative to the origin (top left corner of the border box) of the frame, or, |
44 | | * if the frame has continuations, the origin of the _first_ continuation. |
45 | | */ |
46 | | class PreEffectsVisualOverflowCollector : public nsLayoutUtils::BoxCallback |
47 | | { |
48 | | public: |
49 | | /** |
50 | | * If the pre-effects visual overflow rect of the frame being examined |
51 | | * happens to be known, it can be passed in as aCurrentFrame and its |
52 | | * pre-effects visual overflow rect can be passed in as |
53 | | * aCurrentFrameOverflowArea. This is just an optimization to save a |
54 | | * frame property lookup - these arguments are optional. |
55 | | */ |
56 | | PreEffectsVisualOverflowCollector(nsIFrame* aFirstContinuation, |
57 | | nsIFrame* aCurrentFrame, |
58 | | const nsRect& aCurrentFrameOverflowArea, |
59 | | bool aInReflow) |
60 | | : mFirstContinuation(aFirstContinuation) |
61 | | , mCurrentFrame(aCurrentFrame) |
62 | | , mCurrentFrameOverflowArea(aCurrentFrameOverflowArea) |
63 | | , mInReflow(aInReflow) |
64 | 0 | { |
65 | 0 | NS_ASSERTION(!mFirstContinuation->GetPrevContinuation(), |
66 | 0 | "We want the first continuation here"); |
67 | 0 | } |
68 | | |
69 | 0 | virtual void AddBox(nsIFrame* aFrame) override { |
70 | 0 | nsRect overflow = (aFrame == mCurrentFrame) |
71 | 0 | ? mCurrentFrameOverflowArea |
72 | 0 | : GetPreEffectsVisualOverflowRect(aFrame, mInReflow); |
73 | 0 | mResult.UnionRect(mResult, overflow + aFrame->GetOffsetTo(mFirstContinuation)); |
74 | 0 | } |
75 | | |
76 | 0 | nsRect GetResult() const { |
77 | 0 | return mResult; |
78 | 0 | } |
79 | | |
80 | | private: |
81 | | |
82 | | static nsRect GetPreEffectsVisualOverflowRect(nsIFrame* aFrame, |
83 | 0 | bool aInReflow) { |
84 | 0 | nsRect* r = aFrame->GetProperty(nsIFrame::PreEffectsBBoxProperty()); |
85 | 0 | if (r) { |
86 | 0 | return *r; |
87 | 0 | } |
88 | 0 | |
89 | | #ifdef DEBUG |
90 | | // Having PreTransformOverflowAreasProperty cached means |
91 | | // GetVisualOverflowRect() will return post-effect rect, which is not what |
92 | | // we want. This function intentional reports pre-effect rect. But it does |
93 | | // not matter if there is no SVG effect on this frame, since no effect |
94 | | // means post-effect rect matches pre-effect rect. |
95 | | // |
96 | | // This function may be called during reflow or painting. We should only |
97 | | // do this check in painting process since the PreEffectsBBoxProperty of |
98 | | // continuations are not set correctly while reflowing. |
99 | | if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame) && !aInReflow) { |
100 | | nsOverflowAreas* preTransformOverflows = |
101 | | aFrame->GetProperty(aFrame->PreTransformOverflowAreasProperty()); |
102 | | |
103 | | MOZ_ASSERT(!preTransformOverflows, |
104 | | "GetVisualOverflowRect() won't return the pre-effects rect!"); |
105 | | } |
106 | | #endif |
107 | 0 | return aFrame->GetVisualOverflowRect(); |
108 | 0 | } |
109 | | |
110 | | nsIFrame* mFirstContinuation; |
111 | | nsIFrame* mCurrentFrame; |
112 | | const nsRect& mCurrentFrameOverflowArea; |
113 | | nsRect mResult; |
114 | | bool mInReflow; |
115 | | }; |
116 | | |
117 | | /** |
118 | | * Gets the union of the pre-effects visual overflow rects of all of a frame's |
119 | | * continuations, in "user space". |
120 | | */ |
121 | | static nsRect |
122 | | GetPreEffectsVisualOverflowUnion(nsIFrame* aFirstContinuation, |
123 | | nsIFrame* aCurrentFrame, |
124 | | const nsRect& aCurrentFramePreEffectsOverflow, |
125 | | const nsPoint& aFirstContinuationToUserSpace, |
126 | | bool aInReflow) |
127 | 0 | { |
128 | 0 | NS_ASSERTION(!aFirstContinuation->GetPrevContinuation(), |
129 | 0 | "Need first continuation here"); |
130 | 0 | PreEffectsVisualOverflowCollector collector(aFirstContinuation, |
131 | 0 | aCurrentFrame, |
132 | 0 | aCurrentFramePreEffectsOverflow, |
133 | 0 | aInReflow); |
134 | 0 | // Compute union of all overflow areas relative to aFirstContinuation: |
135 | 0 | nsLayoutUtils::GetAllInFlowBoxes(aFirstContinuation, &collector); |
136 | 0 | // Return the result in user space: |
137 | 0 | return collector.GetResult() + aFirstContinuationToUserSpace; |
138 | 0 | } |
139 | | |
140 | | /** |
141 | | * Gets the pre-effects visual overflow rect of aCurrentFrame in "user space". |
142 | | */ |
143 | | static nsRect |
144 | | GetPreEffectsVisualOverflow(nsIFrame* aFirstContinuation, |
145 | | nsIFrame* aCurrentFrame, |
146 | | const nsPoint& aFirstContinuationToUserSpace) |
147 | 0 | { |
148 | 0 | NS_ASSERTION(!aFirstContinuation->GetPrevContinuation(), |
149 | 0 | "Need first continuation here"); |
150 | 0 | PreEffectsVisualOverflowCollector collector(aFirstContinuation, |
151 | 0 | nullptr, |
152 | 0 | nsRect(), |
153 | 0 | false); |
154 | 0 | // Compute overflow areas of current frame relative to aFirstContinuation: |
155 | 0 | nsLayoutUtils::AddBoxesForFrame(aCurrentFrame, &collector); |
156 | 0 | // Return the result in user space: |
157 | 0 | return collector.GetResult() + aFirstContinuationToUserSpace; |
158 | 0 | } |
159 | | |
160 | | bool |
161 | | nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame) |
162 | 0 | { |
163 | 0 | // Even when SVG display lists are disabled, returning true for SVG frames |
164 | 0 | // does not adversely affect any of our callers. Therefore we don't bother |
165 | 0 | // checking the SDL prefs here, since we don't know if we're being called for |
166 | 0 | // painting or hit-testing anyway. |
167 | 0 | const nsStyleSVGReset *style = aFrame->StyleSVGReset(); |
168 | 0 | return aFrame->StyleEffects()->HasFilters() || |
169 | 0 | style->HasClipPath() || style->HasMask(); |
170 | 0 | } |
171 | | |
172 | | bool |
173 | | nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(const nsIFrame* aFrame) |
174 | 0 | { |
175 | 0 | const nsStyleSVGReset *style = aFrame->StyleSVGReset(); |
176 | 0 | return style->HasClipPath() || style->HasMask(); |
177 | 0 | } |
178 | | |
179 | | nsPoint |
180 | | nsSVGIntegrationUtils::GetOffsetToBoundingBox(nsIFrame* aFrame) |
181 | 0 | { |
182 | 0 | if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) { |
183 | 0 | // Do NOT call GetAllInFlowRectsUnion for SVG - it will get the |
184 | 0 | // covered region relative to the nsSVGOuterSVGFrame, which is absolutely |
185 | 0 | // not what we want. SVG frames are always in user space, so they have |
186 | 0 | // no offset adjustment to make. |
187 | 0 | return nsPoint(); |
188 | 0 | } |
189 | 0 | |
190 | 0 | // The GetAllInFlowRectsUnion() call gets the union of the frame border-box |
191 | 0 | // rects over all continuations, relative to the origin (top-left of the |
192 | 0 | // border box) of its second argument (here, aFrame, the first continuation). |
193 | 0 | return -nsLayoutUtils::GetAllInFlowRectsUnion(aFrame, aFrame).TopLeft(); |
194 | 0 | } |
195 | | |
196 | | /* static */ nsSize |
197 | | nsSVGIntegrationUtils::GetContinuationUnionSize(nsIFrame* aNonSVGFrame) |
198 | 0 | { |
199 | 0 | NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG), |
200 | 0 | "SVG frames should not get here"); |
201 | 0 | nsIFrame* firstFrame = |
202 | 0 | nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame); |
203 | 0 | return nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame, firstFrame).Size(); |
204 | 0 | } |
205 | | |
206 | | /* static */ gfx::Size |
207 | | nsSVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(nsIFrame* aNonSVGFrame) |
208 | 0 | { |
209 | 0 | NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG), |
210 | 0 | "SVG frames should not get here"); |
211 | 0 | nsIFrame* firstFrame = |
212 | 0 | nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame); |
213 | 0 | nsRect r = nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame, firstFrame); |
214 | 0 | return gfx::Size(nsPresContext::AppUnitsToFloatCSSPixels(r.width), |
215 | 0 | nsPresContext::AppUnitsToFloatCSSPixels(r.height)); |
216 | 0 | } |
217 | | |
218 | | gfxRect |
219 | | nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame, |
220 | | bool aUnionContinuations) |
221 | 0 | { |
222 | 0 | // Except for nsSVGOuterSVGFrame, we shouldn't be getting here with SVG |
223 | 0 | // frames at all. This function is for elements that are laid out using the |
224 | 0 | // CSS box model rules. |
225 | 0 | NS_ASSERTION(!(aNonSVGFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT), |
226 | 0 | "Frames with SVG layout should not get here"); |
227 | 0 | MOZ_ASSERT(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG) || |
228 | 0 | aNonSVGFrame->IsSVGOuterSVGFrame()); |
229 | 0 |
|
230 | 0 | nsIFrame* firstFrame = |
231 | 0 | nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame); |
232 | 0 | // 'r' is in "user space": |
233 | 0 | nsRect r = (aUnionContinuations) |
234 | 0 | ? GetPreEffectsVisualOverflowUnion(firstFrame, nullptr, nsRect(), |
235 | 0 | GetOffsetToBoundingBox(firstFrame), |
236 | 0 | false) |
237 | 0 | : GetPreEffectsVisualOverflow(firstFrame, aNonSVGFrame, |
238 | 0 | GetOffsetToBoundingBox(firstFrame)); |
239 | 0 |
|
240 | 0 | return nsLayoutUtils::RectToGfxRect(r, |
241 | 0 | AppUnitsPerCSSPixel()); |
242 | 0 | } |
243 | | |
244 | | // XXX Since we're called during reflow, this method is broken for frames with |
245 | | // continuations. When we're called for a frame with continuations, we're |
246 | | // called for each continuation in turn as it's reflowed. However, it isn't |
247 | | // until the last continuation is reflowed that this method's |
248 | | // GetOffsetToBoundingBox() and GetPreEffectsVisualOverflowUnion() calls will |
249 | | // obtain valid border boxes for all the continuations. As a result, we'll |
250 | | // end up returning bogus post-filter visual overflow rects for all the prior |
251 | | // continuations. Unfortunately, by the time the last continuation is |
252 | | // reflowed, it's too late to go back and set and propagate the overflow |
253 | | // rects on the previous continuations. |
254 | | // |
255 | | // The reason that we need to pass an override bbox to |
256 | | // GetPreEffectsVisualOverflowUnion rather than just letting it call into our |
257 | | // GetSVGBBoxForNonSVGFrame method is because we get called by |
258 | | // ComputeEffectsRect when it has been called with |
259 | | // aStoreRectProperties set to false. In this case the pre-effects visual |
260 | | // overflow rect that it has been passed may be different to that stored on |
261 | | // aFrame, resulting in a different bbox. |
262 | | // |
263 | | // XXXjwatt The pre-effects visual overflow rect passed to |
264 | | // ComputeEffectsRect won't include continuation overflows, so |
265 | | // for frames with continuation the following filter analysis will likely end |
266 | | // up being carried out with a bbox created as if the frame didn't have |
267 | | // continuations. |
268 | | // |
269 | | // XXXjwatt Using aPreEffectsOverflowRect to create the bbox isn't really right |
270 | | // for SVG frames, since for SVG frames the SVG spec defines the bbox to be |
271 | | // something quite different to the pre-effects visual overflow rect. However, |
272 | | // we're essentially calculating an invalidation area here, and using the |
273 | | // pre-effects overflow rect will actually overestimate that area which, while |
274 | | // being a bit wasteful, isn't otherwise a problem. |
275 | | // |
276 | | nsRect |
277 | | nsSVGIntegrationUtils:: |
278 | | ComputePostEffectsVisualOverflowRect(nsIFrame* aFrame, |
279 | | const nsRect& aPreEffectsOverflowRect) |
280 | 0 | { |
281 | 0 | NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT), |
282 | 0 | "Don't call this on SVG child frames"); |
283 | 0 |
|
284 | 0 | nsIFrame* firstFrame = |
285 | 0 | nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame); |
286 | 0 | SVGObserverUtils::EffectProperties effectProperties = |
287 | 0 | SVGObserverUtils::GetEffectProperties(firstFrame); |
288 | 0 | if (!effectProperties.HasValidFilter()) { |
289 | 0 | return aPreEffectsOverflowRect; |
290 | 0 | } |
291 | 0 | |
292 | 0 | // Create an override bbox - see comment above: |
293 | 0 | nsPoint firstFrameToBoundingBox = GetOffsetToBoundingBox(firstFrame); |
294 | 0 | // overrideBBox is in "user space", in _CSS_ pixels: |
295 | 0 | // XXX Why are we rounding out to pixel boundaries? We don't do that in |
296 | 0 | // GetSVGBBoxForNonSVGFrame, and it doesn't appear to be necessary. |
297 | 0 | gfxRect overrideBBox = |
298 | 0 | nsLayoutUtils::RectToGfxRect( |
299 | 0 | GetPreEffectsVisualOverflowUnion(firstFrame, aFrame, |
300 | 0 | aPreEffectsOverflowRect, |
301 | 0 | firstFrameToBoundingBox, |
302 | 0 | true), |
303 | 0 | AppUnitsPerCSSPixel()); |
304 | 0 | overrideBBox.RoundOut(); |
305 | 0 |
|
306 | 0 | nsRect overflowRect = |
307 | 0 | nsFilterInstance::GetPostFilterBounds(firstFrame, &overrideBBox); |
308 | 0 |
|
309 | 0 | // Return overflowRect relative to aFrame, rather than "user space": |
310 | 0 | return overflowRect - (aFrame->GetOffsetTo(firstFrame) + firstFrameToBoundingBox); |
311 | 0 | } |
312 | | |
313 | | nsIntRegion |
314 | | nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame, |
315 | | const nsPoint& aToReferenceFrame, |
316 | | const nsIntRegion& aInvalidRegion) |
317 | 0 | { |
318 | 0 | if (aInvalidRegion.IsEmpty()) { |
319 | 0 | return nsIntRect(); |
320 | 0 | } |
321 | 0 | |
322 | 0 | // Don't bother calling GetEffectProperties; the filter property should |
323 | 0 | // already have been set up during reflow/ComputeFrameEffectsRect |
324 | 0 | nsIFrame* firstFrame = |
325 | 0 | nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame); |
326 | 0 | SVGFilterObserverListForCSSProp* observers = |
327 | 0 | SVGObserverUtils::GetFilterObserverList(firstFrame); |
328 | 0 | if (!observers || !observers->IsInObserverLists()) { |
329 | 0 | return aInvalidRegion; |
330 | 0 | } |
331 | 0 | |
332 | 0 | int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel(); |
333 | 0 |
|
334 | 0 | if (!observers || !observers->ReferencesValidResources()) { |
335 | 0 | // The frame is either not there or not currently available, |
336 | 0 | // perhaps because we're in the middle of tearing stuff down. |
337 | 0 | // Be conservative, return our visual overflow rect relative |
338 | 0 | // to the reference frame. |
339 | 0 | nsRect overflow = aFrame->GetVisualOverflowRect() + aToReferenceFrame; |
340 | 0 | return overflow.ToOutsidePixels(appUnitsPerDevPixel); |
341 | 0 | } |
342 | 0 | |
343 | 0 | // Convert aInvalidRegion into bounding box frame space in app units: |
344 | 0 | nsPoint toBoundingBox = |
345 | 0 | aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame); |
346 | 0 | // The initial rect was relative to the reference frame, so we need to |
347 | 0 | // remove that offset to get a rect relative to the current frame. |
348 | 0 | toBoundingBox -= aToReferenceFrame; |
349 | 0 | nsRegion preEffectsRegion = aInvalidRegion.ToAppUnits(appUnitsPerDevPixel).MovedBy(toBoundingBox); |
350 | 0 |
|
351 | 0 | // Adjust the dirty area for effects, and shift it back to being relative to |
352 | 0 | // the reference frame. |
353 | 0 | nsRegion result = nsFilterInstance::GetPostFilterDirtyArea(firstFrame, |
354 | 0 | preEffectsRegion).MovedBy(-toBoundingBox); |
355 | 0 | // Return the result, in pixels relative to the reference frame. |
356 | 0 | return result.ToOutsidePixels(appUnitsPerDevPixel); |
357 | 0 | } |
358 | | |
359 | | nsRect |
360 | | nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame, |
361 | | const nsRect& aDirtyRect) |
362 | 0 | { |
363 | 0 | // Don't bother calling GetEffectProperties; the filter property should |
364 | 0 | // already have been set up during reflow/ComputeFrameEffectsRect |
365 | 0 | nsIFrame* firstFrame = |
366 | 0 | nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame); |
367 | 0 | SVGFilterObserverListForCSSProp* observers = |
368 | 0 | SVGObserverUtils::GetFilterObserverList(firstFrame); |
369 | 0 | if (!observers || !observers->ReferencesValidResources()) { |
370 | 0 | return aDirtyRect; |
371 | 0 | } |
372 | 0 | |
373 | 0 | // Convert aDirtyRect into "user space" in app units: |
374 | 0 | nsPoint toUserSpace = |
375 | 0 | aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame); |
376 | 0 | nsRect postEffectsRect = aDirtyRect + toUserSpace; |
377 | 0 |
|
378 | 0 | // Return ther result, relative to aFrame, not in user space: |
379 | 0 | return nsFilterInstance::GetPreFilterNeededArea(firstFrame, postEffectsRect).GetBounds() |
380 | 0 | - toUserSpace; |
381 | 0 | } |
382 | | |
383 | | bool |
384 | | nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt) |
385 | 0 | { |
386 | 0 | nsIFrame* firstFrame = |
387 | 0 | nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame); |
388 | 0 | // Convert aPt to user space: |
389 | 0 | nsPoint toUserSpace; |
390 | 0 | if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { |
391 | 0 | // XXXmstange Isn't this wrong for svg:use and innerSVG frames? |
392 | 0 | toUserSpace = aFrame->GetPosition(); |
393 | 0 | } else { |
394 | 0 | toUserSpace = |
395 | 0 | aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame); |
396 | 0 | } |
397 | 0 | nsPoint pt = aPt + toUserSpace; |
398 | 0 | gfxPoint userSpacePt = |
399 | 0 | gfxPoint(pt.x, pt.y) / AppUnitsPerCSSPixel(); |
400 | 0 | return nsSVGUtils::HitTestClip(firstFrame, userSpacePt); |
401 | 0 | } |
402 | | |
403 | | class RegularFramePaintCallback : public nsSVGFilterPaintCallback |
404 | | { |
405 | | public: |
406 | | RegularFramePaintCallback(nsDisplayListBuilder* aBuilder, |
407 | | LayerManager* aManager, |
408 | | const gfxPoint& aUserSpaceToFrameSpaceOffset) |
409 | | : mBuilder(aBuilder), mLayerManager(aManager), |
410 | 0 | mUserSpaceToFrameSpaceOffset(aUserSpaceToFrameSpaceOffset) {} |
411 | | |
412 | | virtual void Paint(gfxContext& aContext, nsIFrame *aTarget, |
413 | | const gfxMatrix& aTransform, |
414 | | const nsIntRect* aDirtyRect, |
415 | | imgDrawingParams& aImgParams) override |
416 | 0 | { |
417 | 0 | BasicLayerManager* basic = mLayerManager->AsBasicLayerManager(); |
418 | 0 | RefPtr<gfxContext> oldCtx = basic->GetTarget(); |
419 | 0 | basic->SetTarget(&aContext); |
420 | 0 |
|
421 | 0 | gfxContextMatrixAutoSaveRestore autoSR(&aContext); |
422 | 0 | aContext.SetMatrixDouble(aContext.CurrentMatrixDouble().PreTranslate(-mUserSpaceToFrameSpaceOffset)); |
423 | 0 |
|
424 | 0 | mLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, mBuilder); |
425 | 0 | basic->SetTarget(oldCtx); |
426 | 0 | } |
427 | | |
428 | | private: |
429 | | nsDisplayListBuilder* mBuilder; |
430 | | LayerManager* mLayerManager; |
431 | | gfxPoint mUserSpaceToFrameSpaceOffset; |
432 | | }; |
433 | | |
434 | | typedef nsSVGIntegrationUtils::PaintFramesParams PaintFramesParams; |
435 | | |
436 | | /** |
437 | | * Paint css-positioned-mask onto a given target(aMaskDT). |
438 | | */ |
439 | | static void |
440 | | PaintMaskSurface(const PaintFramesParams& aParams, |
441 | | DrawTarget* aMaskDT, float aOpacity, ComputedStyle* aSC, |
442 | | const nsTArray<nsSVGMaskFrame*>& aMaskFrames, |
443 | | const Matrix& aMaskSurfaceMatrix, |
444 | | const nsPoint& aOffsetToUserSpace) |
445 | 0 | { |
446 | 0 | MOZ_ASSERT(aMaskFrames.Length() > 0); |
447 | 0 | MOZ_ASSERT(aMaskDT->GetFormat() == SurfaceFormat::A8); |
448 | 0 | MOZ_ASSERT(aOpacity == 1.0 || aMaskFrames.Length() == 1); |
449 | 0 |
|
450 | 0 | const nsStyleSVGReset *svgReset = aSC->StyleSVGReset(); |
451 | 0 | gfxMatrix cssPxToDevPxMatrix = |
452 | 0 | nsSVGUtils::GetCSSPxToDevPxMatrix(aParams.frame); |
453 | 0 |
|
454 | 0 | nsPresContext* presContext = aParams.frame->PresContext(); |
455 | 0 | gfxPoint devPixelOffsetToUserSpace = |
456 | 0 | nsLayoutUtils::PointToGfxPoint(aOffsetToUserSpace, |
457 | 0 | presContext->AppUnitsPerDevPixel()); |
458 | 0 |
|
459 | 0 | RefPtr<gfxContext> maskContext = gfxContext::CreateOrNull(aMaskDT); |
460 | 0 | MOZ_ASSERT(maskContext); |
461 | 0 | maskContext->SetMatrix(aMaskSurfaceMatrix); |
462 | 0 |
|
463 | 0 | // Multiple SVG masks interleave with image mask. Paint each layer onto |
464 | 0 | // aMaskDT one at a time. |
465 | 0 | for (int i = aMaskFrames.Length() - 1; i >= 0 ; i--) { |
466 | 0 | nsSVGMaskFrame *maskFrame = aMaskFrames[i]; |
467 | 0 | CompositionOp compositionOp = (i == int(aMaskFrames.Length() - 1)) |
468 | 0 | ? CompositionOp::OP_OVER |
469 | 0 | : nsCSSRendering::GetGFXCompositeMode(svgReset->mMask.mLayers[i].mComposite); |
470 | 0 |
|
471 | 0 | // maskFrame != nullptr means we get a SVG mask. |
472 | 0 | // maskFrame == nullptr means we get an image mask. |
473 | 0 | if (maskFrame) { |
474 | 0 | Matrix svgMaskMatrix; |
475 | 0 | nsSVGMaskFrame::MaskParams params(maskContext, aParams.frame, |
476 | 0 | cssPxToDevPxMatrix, |
477 | 0 | aOpacity, &svgMaskMatrix, |
478 | 0 | svgReset->mMask.mLayers[i].mMaskMode, |
479 | 0 | aParams.imgParams); |
480 | 0 | RefPtr<SourceSurface> svgMask = maskFrame->GetMaskForMaskedFrame(params); |
481 | 0 | if (svgMask) { |
482 | 0 | gfxContextMatrixAutoSaveRestore matRestore(maskContext); |
483 | 0 |
|
484 | 0 | maskContext->Multiply(ThebesMatrix(svgMaskMatrix)); |
485 | 0 | aMaskDT->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)), svgMask, |
486 | 0 | Point(0, 0), |
487 | 0 | DrawOptions(1.0, compositionOp)); |
488 | 0 | } |
489 | 0 | } else if (svgReset->mMask.mLayers[i].mImage.IsResolved()) { |
490 | 0 | gfxContextMatrixAutoSaveRestore matRestore(maskContext); |
491 | 0 |
|
492 | 0 | maskContext->Multiply(gfxMatrix::Translation(-devPixelOffsetToUserSpace)); |
493 | 0 | nsCSSRendering::PaintBGParams params = |
494 | 0 | nsCSSRendering::PaintBGParams::ForSingleLayer(*presContext, |
495 | 0 | aParams.dirtyRect, |
496 | 0 | aParams.borderArea, |
497 | 0 | aParams.frame, |
498 | 0 | aParams.builder->GetBackgroundPaintFlags() | |
499 | 0 | nsCSSRendering::PAINTBG_MASK_IMAGE, |
500 | 0 | i, compositionOp, |
501 | 0 | aOpacity); |
502 | 0 |
|
503 | 0 | aParams.imgParams.result &= |
504 | 0 | nsCSSRendering::PaintStyleImageLayerWithSC(params, *maskContext, aSC, |
505 | 0 | *aParams.frame->StyleBorder()); |
506 | 0 | } else { |
507 | 0 | aParams.imgParams.result &= ImgDrawResult::NOT_READY; |
508 | 0 | } |
509 | 0 | } |
510 | 0 | } |
511 | | |
512 | | struct MaskPaintResult { |
513 | | RefPtr<SourceSurface> maskSurface; |
514 | | Matrix maskTransform; |
515 | | bool transparentBlackMask; |
516 | | bool opacityApplied; |
517 | | |
518 | | MaskPaintResult() |
519 | | : transparentBlackMask(false) |
520 | | , opacityApplied(false) |
521 | 0 | {} |
522 | | }; |
523 | | |
524 | | static MaskPaintResult |
525 | | CreateAndPaintMaskSurface(const PaintFramesParams& aParams, |
526 | | float aOpacity, ComputedStyle* aSC, |
527 | | const nsTArray<nsSVGMaskFrame*>& aMaskFrames, |
528 | | const nsPoint& aOffsetToUserSpace) |
529 | 0 | { |
530 | 0 | const nsStyleSVGReset *svgReset = aSC->StyleSVGReset(); |
531 | 0 | MOZ_ASSERT(aMaskFrames.Length() > 0); |
532 | 0 | MaskPaintResult paintResult; |
533 | 0 |
|
534 | 0 | gfxContext& ctx = aParams.ctx; |
535 | 0 |
|
536 | 0 | // Optimization for single SVG mask. |
537 | 0 | if (((aMaskFrames.Length() == 1) && aMaskFrames[0])) { |
538 | 0 | gfxMatrix cssPxToDevPxMatrix = |
539 | 0 | nsSVGUtils::GetCSSPxToDevPxMatrix(aParams.frame); |
540 | 0 | paintResult.opacityApplied = true; |
541 | 0 | nsSVGMaskFrame::MaskParams params(&ctx, aParams.frame, cssPxToDevPxMatrix, |
542 | 0 | aOpacity, &paintResult.maskTransform, |
543 | 0 | svgReset->mMask.mLayers[0].mMaskMode, |
544 | 0 | aParams.imgParams); |
545 | 0 | paintResult.maskSurface = |
546 | 0 | aMaskFrames[0]->GetMaskForMaskedFrame(params); |
547 | 0 |
|
548 | 0 | if (!paintResult.maskSurface) { |
549 | 0 | paintResult.transparentBlackMask = true; |
550 | 0 | } |
551 | 0 |
|
552 | 0 | return paintResult; |
553 | 0 | } |
554 | 0 |
|
555 | 0 | const IntRect& maskSurfaceRect = aParams.maskRect; |
556 | 0 | if (maskSurfaceRect.IsEmpty()) { |
557 | 0 | paintResult.transparentBlackMask = true; |
558 | 0 | return paintResult; |
559 | 0 | } |
560 | 0 | |
561 | 0 | RefPtr<DrawTarget> maskDT = |
562 | 0 | ctx.GetDrawTarget()->CreateSimilarDrawTarget(maskSurfaceRect.Size(), |
563 | 0 | SurfaceFormat::A8); |
564 | 0 | if (!maskDT || !maskDT->IsValid()) { |
565 | 0 | return paintResult; |
566 | 0 | } |
567 | 0 | |
568 | 0 | // We can paint mask along with opacity only if |
569 | 0 | // 1. There is only one mask, or |
570 | 0 | // 2. No overlap among masks. |
571 | 0 | // Collision detect in #2 is not that trivial, we only accept #1 here. |
572 | 0 | paintResult.opacityApplied = (aMaskFrames.Length() == 1); |
573 | 0 |
|
574 | 0 | // Set context's matrix on maskContext, offset by the maskSurfaceRect's |
575 | 0 | // position. This makes sure that we combine the masks in device space. |
576 | 0 | Matrix maskSurfaceMatrix = |
577 | 0 | ctx.CurrentMatrix() * Matrix::Translation(-aParams.maskRect.TopLeft()); |
578 | 0 |
|
579 | 0 | PaintMaskSurface(aParams, maskDT, |
580 | 0 | paintResult.opacityApplied ? aOpacity : 1.0, |
581 | 0 | aSC, aMaskFrames, maskSurfaceMatrix, |
582 | 0 | aOffsetToUserSpace); |
583 | 0 |
|
584 | 0 | if (aParams.imgParams.result != ImgDrawResult::SUCCESS && |
585 | 0 | aParams.imgParams.result != ImgDrawResult::SUCCESS_NOT_COMPLETE) { |
586 | 0 | // Now we know the status of mask resource since we used it while painting. |
587 | 0 | // According to the return value of PaintMaskSurface, we know whether mask |
588 | 0 | // resource is resolvable or not. |
589 | 0 | // |
590 | 0 | // For a HTML doc: |
591 | 0 | // According to css-masking spec, always create a mask surface when |
592 | 0 | // we have any item in maskFrame even if all of those items are |
593 | 0 | // non-resolvable <mask-sources> or <images>. |
594 | 0 | // Set paintResult.transparentBlackMask as true, the caller should stop |
595 | 0 | // painting masked content as if this mask is a transparent black one. |
596 | 0 | // For a SVG doc: |
597 | 0 | // SVG 1.1 say that if we fail to resolve a mask, we should draw the |
598 | 0 | // object unmasked. |
599 | 0 | // Left paintResult.maskSurface empty, the caller should paint all |
600 | 0 | // masked content as if this mask is an opaque white one(no mask). |
601 | 0 | paintResult.transparentBlackMask = |
602 | 0 | !(aParams.frame->GetStateBits() & NS_FRAME_SVG_LAYOUT); |
603 | 0 |
|
604 | 0 | MOZ_ASSERT(!paintResult.maskSurface); |
605 | 0 | return paintResult; |
606 | 0 | } |
607 | 0 |
|
608 | 0 | paintResult.maskTransform = maskSurfaceMatrix; |
609 | 0 | if (!paintResult.maskTransform.Invert()) { |
610 | 0 | return paintResult; |
611 | 0 | } |
612 | 0 | |
613 | 0 | paintResult.maskSurface = maskDT->Snapshot(); |
614 | 0 | return paintResult; |
615 | 0 | } |
616 | | |
617 | | static bool |
618 | | ValidateSVGFrame(nsIFrame* aFrame) |
619 | 0 | { |
620 | | #ifdef DEBUG |
621 | | NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) || |
622 | | (NS_SVGDisplayListPaintingEnabled() && |
623 | | !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)), |
624 | | "Should not use nsSVGIntegrationUtils on this SVG frame"); |
625 | | #endif |
626 | |
|
627 | 0 | bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT); |
628 | 0 | if (hasSVGLayout) { |
629 | | #ifdef DEBUG |
630 | | nsSVGDisplayableFrame* svgFrame = do_QueryFrame(aFrame); |
631 | | MOZ_ASSERT(svgFrame && aFrame->GetContent()->IsSVGElement(), |
632 | | "A non-SVG frame carries NS_FRAME_SVG_LAYOUT flag?"); |
633 | | #endif |
634 | |
|
635 | 0 | const nsIContent* content = aFrame->GetContent(); |
636 | 0 | if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) { |
637 | 0 | // The SVG spec says not to draw _anything_ |
638 | 0 | return false; |
639 | 0 | } |
640 | 0 | } |
641 | 0 | |
642 | 0 | return true; |
643 | 0 | } |
644 | | |
645 | | struct EffectOffsets { |
646 | | // The offset between the reference frame and the bounding box of the |
647 | | // target frame in app unit. |
648 | | nsPoint offsetToBoundingBox; |
649 | | // The offset between the reference frame and the bounding box of the |
650 | | // target frame in app unit. |
651 | | nsPoint offsetToUserSpace; |
652 | | // The offset between the reference frame and the bounding box of the |
653 | | // target frame in device unit. |
654 | | gfxPoint offsetToUserSpaceInDevPx; |
655 | | }; |
656 | | |
657 | | static EffectOffsets |
658 | | ComputeEffectOffset(nsIFrame* aFrame, const PaintFramesParams& aParams) |
659 | 0 | { |
660 | 0 | EffectOffsets result; |
661 | 0 |
|
662 | 0 | result.offsetToBoundingBox = |
663 | 0 | aParams.builder->ToReferenceFrame(aFrame) - |
664 | 0 | nsSVGIntegrationUtils::GetOffsetToBoundingBox(aFrame); |
665 | 0 | if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) { |
666 | 0 | /* Snap the offset if the reference frame is not a SVG frame, |
667 | 0 | * since other frames will be snapped to pixel when rendering. */ |
668 | 0 | result.offsetToBoundingBox = |
669 | 0 | nsPoint( |
670 | 0 | aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(result.offsetToBoundingBox.x), |
671 | 0 | aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(result.offsetToBoundingBox.y)); |
672 | 0 | } |
673 | 0 |
|
674 | 0 | // After applying only "aOffsetToBoundingBox", aParams.ctx would have its |
675 | 0 | // origin at the top left corner of frame's bounding box (over all |
676 | 0 | // continuations). |
677 | 0 | // However, SVG painting needs the origin to be located at the origin of the |
678 | 0 | // SVG frame's "user space", i.e. the space in which, for example, the |
679 | 0 | // frame's BBox lives. |
680 | 0 | // SVG geometry frames and foreignObject frames apply their own offsets, so |
681 | 0 | // their position is relative to their user space. So for these frame types, |
682 | 0 | // if we want aParams.ctx to be in user space, we first need to subtract the |
683 | 0 | // frame's position so that SVG painting can later add it again and the |
684 | 0 | // frame is painted in the right place. |
685 | 0 | gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame); |
686 | 0 | nsPoint toUserSpace = |
687 | 0 | nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)), |
688 | 0 | nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y))); |
689 | 0 |
|
690 | 0 | result.offsetToUserSpace = result.offsetToBoundingBox - toUserSpace; |
691 | 0 |
|
692 | | #ifdef DEBUG |
693 | | bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT); |
694 | | NS_ASSERTION(hasSVGLayout || |
695 | | result.offsetToBoundingBox == result.offsetToUserSpace, |
696 | | "For non-SVG frames there shouldn't be any additional offset"); |
697 | | #endif |
698 | |
|
699 | 0 | result.offsetToUserSpaceInDevPx = |
700 | 0 | nsLayoutUtils::PointToGfxPoint(result.offsetToUserSpace, |
701 | 0 | aFrame->PresContext()->AppUnitsPerDevPixel()); |
702 | 0 |
|
703 | 0 | return result; |
704 | 0 | } |
705 | | |
706 | | /** |
707 | | * Setup transform matrix of a gfx context by a specific frame. Move the |
708 | | * origin of aParams.ctx to the user space of aFrame. |
709 | | */ |
710 | | static EffectOffsets |
711 | | MoveContextOriginToUserSpace(nsIFrame* aFrame, const PaintFramesParams& aParams) |
712 | 0 | { |
713 | 0 | EffectOffsets offset = ComputeEffectOffset(aFrame, aParams); |
714 | 0 |
|
715 | 0 | aParams.ctx.SetMatrixDouble( |
716 | 0 | aParams.ctx.CurrentMatrixDouble().PreTranslate(offset.offsetToUserSpaceInDevPx)); |
717 | 0 |
|
718 | 0 | return offset; |
719 | 0 | } |
720 | | |
721 | | bool |
722 | | nsSVGIntegrationUtils::IsMaskResourceReady(nsIFrame* aFrame) |
723 | 0 | { |
724 | 0 | nsIFrame* firstFrame = |
725 | 0 | nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame); |
726 | 0 | SVGObserverUtils::EffectProperties effectProperties = |
727 | 0 | SVGObserverUtils::GetEffectProperties(firstFrame); |
728 | 0 | nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames(); |
729 | 0 | const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset(); |
730 | 0 |
|
731 | 0 | for (uint32_t i = 0; i < maskFrames.Length(); i++) { |
732 | 0 | // Refers to a valid SVG mask. |
733 | 0 | if (maskFrames[i]) { |
734 | 0 | continue; |
735 | 0 | } |
736 | 0 | |
737 | 0 | // Refers to an external resource, which is not ready yet. |
738 | 0 | if (!svgReset->mMask.mLayers[i].mImage.IsComplete()) { |
739 | 0 | return false; |
740 | 0 | } |
741 | 0 | } |
742 | 0 |
|
743 | 0 | // Either all mask resources are ready, or no mask resource is needed. |
744 | 0 | return true; |
745 | 0 | } |
746 | | |
747 | | class AutoPopGroup |
748 | | { |
749 | | public: |
750 | 0 | AutoPopGroup() : mContext(nullptr) { } |
751 | | |
752 | 0 | ~AutoPopGroup() { |
753 | 0 | if (mContext) { |
754 | 0 | mContext->PopGroupAndBlend(); |
755 | 0 | } |
756 | 0 | } |
757 | | |
758 | 0 | void SetContext(gfxContext* aContext) { |
759 | 0 | mContext = aContext; |
760 | 0 | } |
761 | | |
762 | | private: |
763 | | gfxContext* mContext; |
764 | | }; |
765 | | |
766 | | bool |
767 | | nsSVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams) |
768 | 0 | { |
769 | 0 | nsSVGUtils::MaskUsage maskUsage; |
770 | 0 | nsSVGUtils::DetermineMaskUsage(aParams.frame, aParams.handleOpacity, |
771 | 0 | maskUsage); |
772 | 0 | if (!maskUsage.shouldDoSomething()) { |
773 | 0 | return false; |
774 | 0 | } |
775 | 0 | |
776 | 0 | nsIFrame* frame = aParams.frame; |
777 | 0 | if (!ValidateSVGFrame(frame)) { |
778 | 0 | return false; |
779 | 0 | } |
780 | 0 | |
781 | 0 | gfxContext& ctx = aParams.ctx; |
782 | 0 | nsIFrame* firstFrame = |
783 | 0 | nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame); |
784 | 0 | SVGObserverUtils::EffectProperties effectProperties = |
785 | 0 | SVGObserverUtils::GetEffectProperties(firstFrame); |
786 | 0 |
|
787 | 0 | RefPtr<DrawTarget> maskTarget = ctx.GetDrawTarget(); |
788 | 0 |
|
789 | 0 | if (maskUsage.shouldGenerateMaskLayer && |
790 | 0 | (maskUsage.shouldGenerateClipMaskLayer || |
791 | 0 | maskUsage.shouldApplyClipPath)) { |
792 | 0 | // We will paint both mask of positioned mask and clip-path into |
793 | 0 | // maskTarget. |
794 | 0 | // |
795 | 0 | // Create one extra draw target for drawing positioned mask, so that we do |
796 | 0 | // not have to copy the content of maskTarget before painting |
797 | 0 | // clip-path into it. |
798 | 0 | maskTarget = maskTarget->CreateSimilarDrawTarget(maskTarget->GetSize(), |
799 | 0 | SurfaceFormat::A8); |
800 | 0 | } |
801 | 0 |
|
802 | 0 | nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames(); |
803 | 0 | AutoPopGroup autoPop; |
804 | 0 | bool shouldPushOpacity = (maskUsage.opacity != 1.0) && |
805 | 0 | (maskFrames.Length() != 1); |
806 | 0 | if (shouldPushOpacity) { |
807 | 0 | ctx.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, maskUsage.opacity); |
808 | 0 | autoPop.SetContext(&ctx); |
809 | 0 | } |
810 | 0 |
|
811 | 0 | gfxContextMatrixAutoSaveRestore matSR; |
812 | 0 |
|
813 | 0 | // Paint clip-path-basic-shape onto ctx |
814 | 0 | gfxContextAutoSaveRestore basicShapeSR; |
815 | 0 | if (maskUsage.shouldApplyBasicShapeOrPath) { |
816 | 0 | matSR.SetContext(&ctx); |
817 | 0 |
|
818 | 0 | MoveContextOriginToUserSpace(firstFrame, aParams); |
819 | 0 |
|
820 | 0 | basicShapeSR.SetContext(&ctx); |
821 | 0 | nsCSSClipPathInstance::ApplyBasicShapeOrPathClip(ctx, frame); |
822 | 0 | if (!maskUsage.shouldGenerateMaskLayer) { |
823 | 0 | // Only have basic-shape clip-path effect. Fill clipped region by |
824 | 0 | // opaque white. |
825 | 0 | ctx.SetColor(Color(1.0, 1.0, 1.0, 1.0)); |
826 | 0 | ctx.Fill(); |
827 | 0 |
|
828 | 0 | return true; |
829 | 0 | } |
830 | 0 | } |
831 | 0 | |
832 | 0 | // Paint mask onto ctx. |
833 | 0 | if (maskUsage.shouldGenerateMaskLayer) { |
834 | 0 | matSR.Restore(); |
835 | 0 | matSR.SetContext(&ctx); |
836 | 0 |
|
837 | 0 | EffectOffsets offsets = MoveContextOriginToUserSpace(frame, aParams); |
838 | 0 | PaintMaskSurface(aParams, maskTarget, |
839 | 0 | shouldPushOpacity ? 1.0 : maskUsage.opacity, |
840 | 0 | firstFrame->Style(), maskFrames, |
841 | 0 | ctx.CurrentMatrix(), |
842 | 0 | offsets.offsetToUserSpace); |
843 | 0 | } |
844 | 0 |
|
845 | 0 | // Paint clip-path onto ctx. |
846 | 0 | if (maskUsage.shouldGenerateClipMaskLayer || maskUsage.shouldApplyClipPath) { |
847 | 0 | matSR.Restore(); |
848 | 0 | matSR.SetContext(&ctx); |
849 | 0 |
|
850 | 0 | MoveContextOriginToUserSpace(firstFrame, aParams); |
851 | 0 | Matrix clipMaskTransform; |
852 | 0 | gfxMatrix cssPxToDevPxMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(frame); |
853 | 0 |
|
854 | 0 | nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(); |
855 | 0 | RefPtr<SourceSurface> maskSurface = |
856 | 0 | maskUsage.shouldGenerateMaskLayer ? maskTarget->Snapshot() : nullptr; |
857 | 0 | clipPathFrame->PaintClipMask(ctx, frame, cssPxToDevPxMatrix, |
858 | 0 | &clipMaskTransform, maskSurface, |
859 | 0 | ctx.CurrentMatrix()); |
860 | 0 | } |
861 | 0 |
|
862 | 0 | return true; |
863 | 0 | } |
864 | | |
865 | | template<class T> |
866 | | void PaintMaskAndClipPathInternal(const PaintFramesParams& aParams, const T& aPaintChild) |
867 | 0 | { |
868 | 0 | MOZ_ASSERT(nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(aParams.frame), |
869 | 0 | "Should not use this method when no mask or clipPath effect" |
870 | 0 | "on this frame"); |
871 | 0 |
|
872 | 0 | /* SVG defines the following rendering model: |
873 | 0 | * |
874 | 0 | * 1. Render geometry |
875 | 0 | * 2. Apply filter |
876 | 0 | * 3. Apply clipping, masking, group opacity |
877 | 0 | * |
878 | 0 | * We handle #3 here and perform a couple of optimizations: |
879 | 0 | * |
880 | 0 | * + Use cairo's clipPath when representable natively (single object |
881 | 0 | * clip region). |
882 | 0 | * |
883 | 0 | * + Merge opacity and masking if both used together. |
884 | 0 | */ |
885 | 0 | nsIFrame* frame = aParams.frame; |
886 | 0 | if (!ValidateSVGFrame(frame)) { |
887 | 0 | return; |
888 | 0 | } |
889 | 0 | |
890 | 0 | nsSVGUtils::MaskUsage maskUsage; |
891 | 0 | nsSVGUtils::DetermineMaskUsage(aParams.frame, aParams.handleOpacity, |
892 | 0 | maskUsage); |
893 | 0 |
|
894 | 0 | if (maskUsage.opacity == 0.0f) { |
895 | 0 | return; |
896 | 0 | } |
897 | 0 | |
898 | 0 | gfxContext& context = aParams.ctx; |
899 | 0 | gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context); |
900 | 0 |
|
901 | 0 | /* Properties are added lazily and may have been removed by a restyle, |
902 | 0 | so make sure all applicable ones are set again. */ |
903 | 0 | nsIFrame* firstFrame = |
904 | 0 | nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame); |
905 | 0 | SVGObserverUtils::EffectProperties effectProperties = |
906 | 0 | SVGObserverUtils::GetEffectProperties(firstFrame); |
907 | 0 |
|
908 | 0 | nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(); |
909 | 0 |
|
910 | 0 | gfxMatrix cssPxToDevPxMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(frame); |
911 | 0 | nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames(); |
912 | 0 |
|
913 | 0 | bool shouldGenerateMask = (maskUsage.opacity != 1.0f || |
914 | 0 | maskUsage.shouldGenerateClipMaskLayer || |
915 | 0 | maskUsage.shouldGenerateMaskLayer); |
916 | 0 | bool shouldPushMask = false; |
917 | 0 |
|
918 | 0 | /* Check if we need to do additional operations on this child's |
919 | 0 | * rendering, which necessitates rendering into another surface. */ |
920 | 0 | if (shouldGenerateMask) { |
921 | 0 | gfxContextMatrixAutoSaveRestore matSR; |
922 | 0 |
|
923 | 0 | Matrix maskTransform; |
924 | 0 | RefPtr<SourceSurface> maskSurface; |
925 | 0 | bool opacityApplied = false; |
926 | 0 |
|
927 | 0 | if (maskUsage.shouldGenerateMaskLayer) { |
928 | 0 | matSR.SetContext(&context); |
929 | 0 |
|
930 | 0 | // For css-mask, we want to generate a mask for each continuation frame, |
931 | 0 | // so we setup context matrix by the position of the current frame, |
932 | 0 | // instead of the first continuation frame. |
933 | 0 | EffectOffsets offsets = MoveContextOriginToUserSpace(frame, aParams); |
934 | 0 | MaskPaintResult paintResult = |
935 | 0 | CreateAndPaintMaskSurface(aParams, maskUsage.opacity, |
936 | 0 | firstFrame->Style(), |
937 | 0 | maskFrames, offsets.offsetToUserSpace); |
938 | 0 |
|
939 | 0 | if (paintResult.transparentBlackMask) { |
940 | 0 | return; |
941 | 0 | } |
942 | 0 | |
943 | 0 | maskSurface = paintResult.maskSurface; |
944 | 0 | if (maskSurface) { |
945 | 0 | shouldPushMask = true; |
946 | 0 | maskTransform = paintResult.maskTransform; |
947 | 0 | opacityApplied = paintResult.opacityApplied; |
948 | 0 | } |
949 | 0 | } |
950 | 0 |
|
951 | 0 | if (maskUsage.shouldGenerateClipMaskLayer) { |
952 | 0 | matSR.Restore(); |
953 | 0 | matSR.SetContext(&context); |
954 | 0 |
|
955 | 0 | MoveContextOriginToUserSpace(firstFrame, aParams); |
956 | 0 | Matrix clipMaskTransform; |
957 | 0 | RefPtr<SourceSurface> clipMaskSurface = |
958 | 0 | clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix, |
959 | 0 | &clipMaskTransform, maskSurface, |
960 | 0 | maskTransform); |
961 | 0 |
|
962 | 0 | if (clipMaskSurface) { |
963 | 0 | maskSurface = clipMaskSurface; |
964 | 0 | maskTransform = clipMaskTransform; |
965 | 0 | } else { |
966 | 0 | // Either entire surface is clipped out, or gfx buffer allocation |
967 | 0 | // failure in nsSVGClipPathFrame::GetClipMask. |
968 | 0 | return; |
969 | 0 | } |
970 | 0 | |
971 | 0 | shouldPushMask = true; |
972 | 0 | } |
973 | 0 |
|
974 | 0 | // opacity != 1.0f. |
975 | 0 | if (!maskUsage.shouldGenerateClipMaskLayer && |
976 | 0 | !maskUsage.shouldGenerateMaskLayer) { |
977 | 0 | MOZ_ASSERT(maskUsage.opacity != 1.0f); |
978 | 0 |
|
979 | 0 | matSR.SetContext(&context); |
980 | 0 | MoveContextOriginToUserSpace(firstFrame, aParams); |
981 | 0 | shouldPushMask = true; |
982 | 0 | } |
983 | 0 |
|
984 | 0 | if (shouldPushMask) { |
985 | 0 | if (aParams.layerManager && aParams.layerManager->GetRoot()->GetContentFlags() & |
986 | 0 | Layer::CONTENT_COMPONENT_ALPHA) { |
987 | 0 | context.PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA, |
988 | 0 | opacityApplied |
989 | 0 | ? 1.0 |
990 | 0 | : maskUsage.opacity, |
991 | 0 | maskSurface, maskTransform); |
992 | 0 | } else { |
993 | 0 | context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, |
994 | 0 | opacityApplied ? 1.0 : maskUsage.opacity, |
995 | 0 | maskSurface, maskTransform); |
996 | 0 | } |
997 | 0 | } |
998 | 0 | } |
999 | 0 |
|
1000 | 0 | /* If this frame has only a trivial clipPath, set up cairo's clipping now so |
1001 | 0 | * we can just do normal painting and get it clipped appropriately. |
1002 | 0 | */ |
1003 | 0 | if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShapeOrPath) { |
1004 | 0 | gfxContextMatrixAutoSaveRestore matSR(&context); |
1005 | 0 |
|
1006 | 0 | MoveContextOriginToUserSpace(firstFrame, aParams); |
1007 | 0 |
|
1008 | 0 | MOZ_ASSERT(!maskUsage.shouldApplyClipPath || |
1009 | 0 | !maskUsage.shouldApplyBasicShapeOrPath); |
1010 | 0 | if (maskUsage.shouldApplyClipPath) { |
1011 | 0 | clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix); |
1012 | 0 | } else { |
1013 | 0 | nsCSSClipPathInstance::ApplyBasicShapeOrPathClip(context, frame); |
1014 | 0 | } |
1015 | 0 | } |
1016 | 0 |
|
1017 | 0 | /* Paint the child */ |
1018 | 0 | context.SetMatrix(matrixAutoSaveRestore.Matrix()); |
1019 | 0 | aPaintChild(); |
1020 | 0 |
|
1021 | 0 | if (gfxPrefs::DrawMaskLayer()) { |
1022 | 0 | gfxContextAutoSaveRestore saver(&context); |
1023 | 0 |
|
1024 | 0 | context.NewPath(); |
1025 | 0 | gfxRect drawingRect = |
1026 | 0 | nsLayoutUtils::RectToGfxRect(aParams.borderArea, |
1027 | 0 | frame->PresContext()->AppUnitsPerDevPixel()); |
1028 | 0 | context.Rectangle(drawingRect, true); |
1029 | 0 | Color overlayColor(0.0f, 0.0f, 0.0f, 0.8f); |
1030 | 0 | if (maskUsage.shouldGenerateMaskLayer) { |
1031 | 0 | overlayColor.r = 1.0f; // red represents css positioned mask. |
1032 | 0 | } |
1033 | 0 | if (maskUsage.shouldApplyClipPath || |
1034 | 0 | maskUsage.shouldGenerateClipMaskLayer) { |
1035 | 0 | overlayColor.g = 1.0f; // green represents clip-path:<clip-source>. |
1036 | 0 | } |
1037 | 0 | if (maskUsage.shouldApplyBasicShapeOrPath) { |
1038 | 0 | overlayColor.b = 1.0f; // blue represents |
1039 | 0 | // clip-path:<basic-shape>||<geometry-box>. |
1040 | 0 | } |
1041 | 0 |
|
1042 | 0 | context.SetColor(overlayColor); |
1043 | 0 | context.Fill(); |
1044 | 0 | } |
1045 | 0 |
|
1046 | 0 | if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShapeOrPath) { |
1047 | 0 | context.PopClip(); |
1048 | 0 | } |
1049 | 0 |
|
1050 | 0 | if (shouldPushMask) { |
1051 | 0 | context.PopGroupAndBlend(); |
1052 | 0 | } |
1053 | 0 |
|
1054 | 0 | } Unexecuted instantiation: Unified_cpp_layout_svg1.cpp:void PaintMaskAndClipPathInternal<nsSVGIntegrationUtils::PaintMaskAndClipPath(nsSVGIntegrationUtils::PaintFramesParams const&)::$_0>(nsSVGIntegrationUtils::PaintFramesParams const&, nsSVGIntegrationUtils::PaintMaskAndClipPath(nsSVGIntegrationUtils::PaintFramesParams const&)::$_0 const&) Unexecuted instantiation: void PaintMaskAndClipPathInternal<std::__1::function<void ()> >(nsSVGIntegrationUtils::PaintFramesParams const&, std::__1::function<void ()> const&) |
1055 | | |
1056 | | |
1057 | | void |
1058 | | nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams) |
1059 | 0 | { |
1060 | 0 | PaintMaskAndClipPathInternal(aParams, [&] { |
1061 | 0 | gfxContext& context = aParams.ctx; |
1062 | 0 | BasicLayerManager* basic = aParams.layerManager->AsBasicLayerManager(); |
1063 | 0 | RefPtr<gfxContext> oldCtx = basic->GetTarget(); |
1064 | 0 | basic->SetTarget(&context); |
1065 | 0 | aParams.layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, |
1066 | 0 | aParams.builder); |
1067 | 0 | basic->SetTarget(oldCtx); |
1068 | 0 | }); |
1069 | 0 | } |
1070 | | |
1071 | | void |
1072 | | nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams, const std::function<void()>& aPaintChild) |
1073 | 0 | { |
1074 | 0 | PaintMaskAndClipPathInternal(aParams, aPaintChild); |
1075 | 0 | } |
1076 | | |
1077 | | void |
1078 | | nsSVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams) |
1079 | 0 | { |
1080 | 0 | MOZ_ASSERT(!aParams.builder->IsForGenerateGlyphMask(), |
1081 | 0 | "Filter effect is discarded while generating glyph mask."); |
1082 | 0 | MOZ_ASSERT(aParams.frame->StyleEffects()->HasFilters(), |
1083 | 0 | "Should not use this method when no filter effect on this frame"); |
1084 | 0 |
|
1085 | 0 | nsIFrame* frame = aParams.frame; |
1086 | 0 | if (!ValidateSVGFrame(frame)) { |
1087 | 0 | return; |
1088 | 0 | } |
1089 | 0 | |
1090 | 0 | float opacity = nsSVGUtils::ComputeOpacity(frame, aParams.handleOpacity); |
1091 | 0 | if (opacity == 0.0f) { |
1092 | 0 | return; |
1093 | 0 | } |
1094 | 0 | |
1095 | 0 | /* Properties are added lazily and may have been removed by a restyle, |
1096 | 0 | so make sure all applicable ones are set again. */ |
1097 | 0 | nsIFrame* firstFrame = |
1098 | 0 | nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame); |
1099 | 0 | SVGObserverUtils::EffectProperties effectProperties = |
1100 | 0 | SVGObserverUtils::GetEffectProperties(firstFrame); |
1101 | 0 |
|
1102 | 0 | if (effectProperties.HasInvalidFilter()) { |
1103 | 0 | return; |
1104 | 0 | } |
1105 | 0 | |
1106 | 0 | gfxContext& context = aParams.ctx; |
1107 | 0 |
|
1108 | 0 | gfxContextAutoSaveRestore autoSR(&context); |
1109 | 0 | EffectOffsets offsets = MoveContextOriginToUserSpace(firstFrame, aParams); |
1110 | 0 |
|
1111 | 0 | /* Paint the child and apply filters */ |
1112 | 0 | RegularFramePaintCallback callback(aParams.builder, aParams.layerManager, |
1113 | 0 | offsets.offsetToUserSpaceInDevPx); |
1114 | 0 | nsRegion dirtyRegion = aParams.dirtyRect - offsets.offsetToBoundingBox; |
1115 | 0 |
|
1116 | 0 | nsFilterInstance::PaintFilteredFrame(frame, &context, &callback, |
1117 | 0 | &dirtyRegion, aParams.imgParams, opacity); |
1118 | 0 | } |
1119 | | |
1120 | | class PaintFrameCallback : public gfxDrawingCallback { |
1121 | | public: |
1122 | | PaintFrameCallback(nsIFrame* aFrame, |
1123 | | const nsSize aPaintServerSize, |
1124 | | const IntSize aRenderSize, |
1125 | | uint32_t aFlags) |
1126 | | : mFrame(aFrame) |
1127 | | , mPaintServerSize(aPaintServerSize) |
1128 | | , mRenderSize(aRenderSize) |
1129 | | , mFlags (aFlags) |
1130 | 0 | {} |
1131 | | virtual bool operator()(gfxContext* aContext, |
1132 | | const gfxRect& aFillRect, |
1133 | | const SamplingFilter aSamplingFilter, |
1134 | | const gfxMatrix& aTransform) override; |
1135 | | private: |
1136 | | nsIFrame* mFrame; |
1137 | | nsSize mPaintServerSize; |
1138 | | IntSize mRenderSize; |
1139 | | uint32_t mFlags; |
1140 | | }; |
1141 | | |
1142 | | bool |
1143 | | PaintFrameCallback::operator()(gfxContext* aContext, |
1144 | | const gfxRect& aFillRect, |
1145 | | const SamplingFilter aSamplingFilter, |
1146 | | const gfxMatrix& aTransform) |
1147 | 0 | { |
1148 | 0 | if (mFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER) |
1149 | 0 | return false; |
1150 | 0 | |
1151 | 0 | AutoSetRestorePaintServerState paintServer(mFrame); |
1152 | 0 |
|
1153 | 0 | aContext->Save(); |
1154 | 0 |
|
1155 | 0 | // Clip to aFillRect so that we don't paint outside. |
1156 | 0 | aContext->NewPath(); |
1157 | 0 | aContext->Rectangle(aFillRect); |
1158 | 0 | aContext->Clip(); |
1159 | 0 |
|
1160 | 0 | gfxMatrix invmatrix = aTransform; |
1161 | 0 | if (!invmatrix.Invert()) { |
1162 | 0 | return false; |
1163 | 0 | } |
1164 | 0 | aContext->Multiply(invmatrix); |
1165 | 0 |
|
1166 | 0 | // nsLayoutUtils::PaintFrame will anchor its painting at mFrame. But we want |
1167 | 0 | // to have it anchored at the top left corner of the bounding box of all of |
1168 | 0 | // mFrame's continuations. So we add a translation transform. |
1169 | 0 | int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); |
1170 | 0 | nsPoint offset = nsSVGIntegrationUtils::GetOffsetToBoundingBox(mFrame); |
1171 | 0 | gfxPoint devPxOffset = gfxPoint(offset.x, offset.y) / appUnitsPerDevPixel; |
1172 | 0 | aContext->Multiply(gfxMatrix::Translation(devPxOffset)); |
1173 | 0 |
|
1174 | 0 | gfxSize paintServerSize = |
1175 | 0 | gfxSize(mPaintServerSize.width, mPaintServerSize.height) / |
1176 | 0 | mFrame->PresContext()->AppUnitsPerDevPixel(); |
1177 | 0 |
|
1178 | 0 | // nsLayoutUtils::PaintFrame wants to render with paintServerSize, but we |
1179 | 0 | // want it to render with mRenderSize, so we need to set up a scale transform. |
1180 | 0 | gfxFloat scaleX = mRenderSize.width / paintServerSize.width; |
1181 | 0 | gfxFloat scaleY = mRenderSize.height / paintServerSize.height; |
1182 | 0 | aContext->Multiply(gfxMatrix::Scaling(scaleX, scaleY)); |
1183 | 0 |
|
1184 | 0 | // Draw. |
1185 | 0 | nsRect dirty(-offset.x, -offset.y, |
1186 | 0 | mPaintServerSize.width, mPaintServerSize.height); |
1187 | 0 |
|
1188 | 0 | using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags; |
1189 | 0 | PaintFrameFlags flags = PaintFrameFlags::PAINT_IN_TRANSFORM; |
1190 | 0 | if (mFlags & nsSVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES) { |
1191 | 0 | flags |= PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES; |
1192 | 0 | } |
1193 | 0 | nsLayoutUtils::PaintFrame(aContext, mFrame, |
1194 | 0 | dirty, NS_RGBA(0, 0, 0, 0), |
1195 | 0 | nsDisplayListBuilderMode::PAINTING, |
1196 | 0 | flags); |
1197 | 0 |
|
1198 | 0 | nsIFrame* currentFrame = mFrame; |
1199 | 0 | while ((currentFrame = currentFrame->GetNextContinuation()) != nullptr) { |
1200 | 0 | offset = currentFrame->GetOffsetToCrossDoc(mFrame); |
1201 | 0 | devPxOffset = gfxPoint(offset.x, offset.y) / appUnitsPerDevPixel; |
1202 | 0 |
|
1203 | 0 | aContext->Save(); |
1204 | 0 | aContext->Multiply(gfxMatrix::Scaling(1/scaleX, 1/scaleY)); |
1205 | 0 | aContext->Multiply(gfxMatrix::Translation(devPxOffset)); |
1206 | 0 | aContext->Multiply(gfxMatrix::Scaling(scaleX, scaleY)); |
1207 | 0 |
|
1208 | 0 | nsLayoutUtils::PaintFrame(aContext, currentFrame, |
1209 | 0 | dirty - offset, NS_RGBA(0, 0, 0, 0), |
1210 | 0 | nsDisplayListBuilderMode::PAINTING, |
1211 | 0 | flags); |
1212 | 0 |
|
1213 | 0 | aContext->Restore(); |
1214 | 0 | } |
1215 | 0 |
|
1216 | 0 | aContext->Restore(); |
1217 | 0 |
|
1218 | 0 | return true; |
1219 | 0 | } |
1220 | | |
1221 | | /* static */ already_AddRefed<gfxDrawable> |
1222 | | nsSVGIntegrationUtils::DrawableFromPaintServer(nsIFrame* aFrame, |
1223 | | nsIFrame* aTarget, |
1224 | | const nsSize& aPaintServerSize, |
1225 | | const IntSize& aRenderSize, |
1226 | | const DrawTarget* aDrawTarget, |
1227 | | const gfxMatrix& aContextMatrix, |
1228 | | uint32_t aFlags) |
1229 | 0 | { |
1230 | 0 | // aPaintServerSize is the size that would be filled when using |
1231 | 0 | // background-repeat:no-repeat and background-size:auto. For normal background |
1232 | 0 | // images, this would be the intrinsic size of the image; for gradients and |
1233 | 0 | // patterns this would be the whole target frame fill area. |
1234 | 0 | // aRenderSize is what we will be actually filling after accounting for |
1235 | 0 | // background-size. |
1236 | 0 | if (aFrame->IsFrameOfType(nsIFrame::eSVGPaintServer)) { |
1237 | 0 | // aFrame is either a pattern or a gradient. These fill the whole target |
1238 | 0 | // frame by default, so aPaintServerSize is the whole target background fill |
1239 | 0 | // area. |
1240 | 0 | nsSVGPaintServerFrame* server = |
1241 | 0 | static_cast<nsSVGPaintServerFrame*>(aFrame); |
1242 | 0 |
|
1243 | 0 | gfxRect overrideBounds(0, 0, |
1244 | 0 | aPaintServerSize.width, aPaintServerSize.height); |
1245 | 0 | overrideBounds.Scale(1.0 / aFrame->PresContext()->AppUnitsPerDevPixel()); |
1246 | 0 | imgDrawingParams imgParams(aFlags); |
1247 | 0 | RefPtr<gfxPattern> pattern = |
1248 | 0 | server->GetPaintServerPattern(aTarget, aDrawTarget, |
1249 | 0 | aContextMatrix, &nsStyleSVG::mFill, 1.0, |
1250 | 0 | imgParams, &overrideBounds); |
1251 | 0 |
|
1252 | 0 | if (!pattern) |
1253 | 0 | return nullptr; |
1254 | 0 | |
1255 | 0 | // pattern is now set up to fill aPaintServerSize. But we want it to |
1256 | 0 | // fill aRenderSize, so we need to add a scaling transform. |
1257 | 0 | // We couldn't just have set overrideBounds to aRenderSize - it would have |
1258 | 0 | // worked for gradients, but for patterns it would result in a different |
1259 | 0 | // pattern size. |
1260 | 0 | gfxFloat scaleX = overrideBounds.Width() / aRenderSize.width; |
1261 | 0 | gfxFloat scaleY = overrideBounds.Height() / aRenderSize.height; |
1262 | 0 | gfxMatrix scaleMatrix = gfxMatrix::Scaling(scaleX, scaleY); |
1263 | 0 | pattern->SetMatrix(scaleMatrix * pattern->GetMatrix()); |
1264 | 0 | RefPtr<gfxDrawable> drawable = |
1265 | 0 | new gfxPatternDrawable(pattern, aRenderSize); |
1266 | 0 | return drawable.forget(); |
1267 | 0 | } |
1268 | 0 | |
1269 | 0 | if (aFrame->IsFrameOfType(nsIFrame::eSVG) && |
1270 | 0 | !static_cast<nsSVGDisplayableFrame*>(do_QueryFrame(aFrame))) { |
1271 | 0 | MOZ_ASSERT_UNREACHABLE("We should prevent painting of unpaintable SVG " |
1272 | 0 | "before we get here"); |
1273 | 0 | return nullptr; |
1274 | 0 | } |
1275 | 0 |
|
1276 | 0 | // We don't want to paint into a surface as long as we don't need to, so we |
1277 | 0 | // set up a drawing callback. |
1278 | 0 | RefPtr<gfxDrawingCallback> cb = |
1279 | 0 | new PaintFrameCallback(aFrame, aPaintServerSize, aRenderSize, aFlags); |
1280 | 0 | RefPtr<gfxDrawable> drawable = new gfxCallbackDrawable(cb, aRenderSize); |
1281 | 0 | return drawable.forget(); |
1282 | 0 | } |