|
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 // This is also necessary to ensure our definition of M_SQRT1_2 is picked up |
|
8 #include "nsSVGUtils.h" |
|
9 #include <algorithm> |
|
10 |
|
11 // Keep others in (case-insensitive) order: |
|
12 #include "gfx2DGlue.h" |
|
13 #include "gfxContext.h" |
|
14 #include "gfxMatrix.h" |
|
15 #include "gfxPlatform.h" |
|
16 #include "gfxRect.h" |
|
17 #include "gfxUtils.h" |
|
18 #include "mozilla/gfx/2D.h" |
|
19 #include "mozilla/Preferences.h" |
|
20 #include "nsCSSFrameConstructor.h" |
|
21 #include "nsDisplayList.h" |
|
22 #include "nsFilterInstance.h" |
|
23 #include "nsFrameList.h" |
|
24 #include "nsGkAtoms.h" |
|
25 #include "nsIContent.h" |
|
26 #include "nsIDocument.h" |
|
27 #include "nsIFrame.h" |
|
28 #include "nsIPresShell.h" |
|
29 #include "nsISVGChildFrame.h" |
|
30 #include "nsPresContext.h" |
|
31 #include "nsRenderingContext.h" |
|
32 #include "nsStyleCoord.h" |
|
33 #include "nsStyleStruct.h" |
|
34 #include "nsSVGClipPathFrame.h" |
|
35 #include "nsSVGContainerFrame.h" |
|
36 #include "nsSVGEffects.h" |
|
37 #include "nsSVGFilterPaintCallback.h" |
|
38 #include "nsSVGForeignObjectFrame.h" |
|
39 #include "gfxSVGGlyphs.h" |
|
40 #include "nsSVGInnerSVGFrame.h" |
|
41 #include "nsSVGIntegrationUtils.h" |
|
42 #include "nsSVGLength2.h" |
|
43 #include "nsSVGMaskFrame.h" |
|
44 #include "nsSVGOuterSVGFrame.h" |
|
45 #include "mozilla/dom/SVGPathElement.h" |
|
46 #include "nsSVGPathGeometryElement.h" |
|
47 #include "nsSVGPathGeometryFrame.h" |
|
48 #include "nsSVGPaintServerFrame.h" |
|
49 #include "mozilla/dom/SVGSVGElement.h" |
|
50 #include "nsTextFrame.h" |
|
51 #include "SVGContentUtils.h" |
|
52 #include "mozilla/unused.h" |
|
53 |
|
54 using namespace mozilla; |
|
55 using namespace mozilla::dom; |
|
56 using namespace mozilla::gfx; |
|
57 |
|
58 static bool sSVGDisplayListHitTestingEnabled; |
|
59 static bool sSVGDisplayListPaintingEnabled; |
|
60 |
|
61 bool |
|
62 NS_SVGDisplayListHitTestingEnabled() |
|
63 { |
|
64 return sSVGDisplayListHitTestingEnabled; |
|
65 } |
|
66 |
|
67 bool |
|
68 NS_SVGDisplayListPaintingEnabled() |
|
69 { |
|
70 return sSVGDisplayListPaintingEnabled; |
|
71 } |
|
72 |
|
73 // we only take the address of this: |
|
74 static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey; |
|
75 |
|
76 SVGAutoRenderState::SVGAutoRenderState(nsRenderingContext *aContext, |
|
77 RenderMode aMode |
|
78 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) |
|
79 : mContext(aContext) |
|
80 , mOriginalRenderState(nullptr) |
|
81 , mMode(aMode) |
|
82 , mPaintingToWindow(false) |
|
83 { |
|
84 MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
|
85 mOriginalRenderState = aContext->RemoveUserData(&sSVGAutoRenderStateKey); |
|
86 // We always remove ourselves from aContext before it dies, so |
|
87 // passing nullptr as the destroy function is okay. |
|
88 aContext->AddUserData(&sSVGAutoRenderStateKey, this, nullptr); |
|
89 } |
|
90 |
|
91 SVGAutoRenderState::~SVGAutoRenderState() |
|
92 { |
|
93 mContext->RemoveUserData(&sSVGAutoRenderStateKey); |
|
94 if (mOriginalRenderState) { |
|
95 mContext->AddUserData(&sSVGAutoRenderStateKey, mOriginalRenderState, nullptr); |
|
96 } |
|
97 } |
|
98 |
|
99 void |
|
100 SVGAutoRenderState::SetPaintingToWindow(bool aPaintingToWindow) |
|
101 { |
|
102 mPaintingToWindow = aPaintingToWindow; |
|
103 } |
|
104 |
|
105 /* static */ SVGAutoRenderState::RenderMode |
|
106 SVGAutoRenderState::GetRenderMode(nsRenderingContext *aContext) |
|
107 { |
|
108 void *state = aContext->GetUserData(&sSVGAutoRenderStateKey); |
|
109 if (state) { |
|
110 return static_cast<SVGAutoRenderState*>(state)->mMode; |
|
111 } |
|
112 return NORMAL; |
|
113 } |
|
114 |
|
115 /* static */ bool |
|
116 SVGAutoRenderState::IsPaintingToWindow(nsRenderingContext *aContext) |
|
117 { |
|
118 void *state = aContext->GetUserData(&sSVGAutoRenderStateKey); |
|
119 if (state) { |
|
120 return static_cast<SVGAutoRenderState*>(state)->mPaintingToWindow; |
|
121 } |
|
122 return false; |
|
123 } |
|
124 |
|
125 void |
|
126 nsSVGUtils::Init() |
|
127 { |
|
128 Preferences::AddBoolVarCache(&sSVGDisplayListHitTestingEnabled, |
|
129 "svg.display-lists.hit-testing.enabled"); |
|
130 |
|
131 Preferences::AddBoolVarCache(&sSVGDisplayListPaintingEnabled, |
|
132 "svg.display-lists.painting.enabled"); |
|
133 } |
|
134 |
|
135 nsSVGDisplayContainerFrame* |
|
136 nsSVGUtils::GetNearestSVGViewport(nsIFrame *aFrame) |
|
137 { |
|
138 NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected"); |
|
139 if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) { |
|
140 return nullptr; |
|
141 } |
|
142 while ((aFrame = aFrame->GetParent())) { |
|
143 NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected"); |
|
144 if (aFrame->GetType() == nsGkAtoms::svgInnerSVGFrame || |
|
145 aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) { |
|
146 return do_QueryFrame(aFrame); |
|
147 } |
|
148 } |
|
149 NS_NOTREACHED("This is not reached. It's only needed to compile."); |
|
150 return nullptr; |
|
151 } |
|
152 |
|
153 nsRect |
|
154 nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame, |
|
155 const nsRect &aPreFilterRect) |
|
156 { |
|
157 NS_ABORT_IF_FALSE(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT, |
|
158 "Called on invalid frame type"); |
|
159 |
|
160 nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame); |
|
161 if (!property || !property->ReferencesValidResources()) { |
|
162 return aPreFilterRect; |
|
163 } |
|
164 |
|
165 return nsFilterInstance::GetPostFilterBounds(aFrame, nullptr, &aPreFilterRect); |
|
166 } |
|
167 |
|
168 bool |
|
169 nsSVGUtils::OuterSVGIsCallingReflowSVG(nsIFrame *aFrame) |
|
170 { |
|
171 return GetOuterSVGFrame(aFrame)->IsCallingReflowSVG(); |
|
172 } |
|
173 |
|
174 bool |
|
175 nsSVGUtils::AnyOuterSVGIsCallingReflowSVG(nsIFrame* aFrame) |
|
176 { |
|
177 nsSVGOuterSVGFrame* outer = GetOuterSVGFrame(aFrame); |
|
178 do { |
|
179 if (outer->IsCallingReflowSVG()) { |
|
180 return true; |
|
181 } |
|
182 outer = GetOuterSVGFrame(outer->GetParent()); |
|
183 } while (outer); |
|
184 return false; |
|
185 } |
|
186 |
|
187 void |
|
188 nsSVGUtils::ScheduleReflowSVG(nsIFrame *aFrame) |
|
189 { |
|
190 NS_ABORT_IF_FALSE(aFrame->IsFrameOfType(nsIFrame::eSVG), |
|
191 "Passed bad frame!"); |
|
192 |
|
193 // If this is triggered, the callers should be fixed to call us before |
|
194 // ReflowSVG is called. If we try to mark dirty bits on frames while we're |
|
195 // in the process of removing them, things will get messed up. |
|
196 NS_ASSERTION(!OuterSVGIsCallingReflowSVG(aFrame), |
|
197 "Do not call under nsISVGChildFrame::ReflowSVG!"); |
|
198 |
|
199 // We don't call nsSVGEffects::InvalidateRenderingObservers here because |
|
200 // we should only be called under InvalidateAndScheduleReflowSVG (which |
|
201 // calls InvalidateBounds) or nsSVGDisplayContainerFrame::InsertFrames |
|
202 // (at which point the frame has no observers). |
|
203 |
|
204 if (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) { |
|
205 return; |
|
206 } |
|
207 |
|
208 if (aFrame->GetStateBits() & |
|
209 (NS_FRAME_IS_DIRTY | NS_FRAME_FIRST_REFLOW)) { |
|
210 // Nothing to do if we're already dirty, or if the outer-<svg> |
|
211 // hasn't yet had its initial reflow. |
|
212 return; |
|
213 } |
|
214 |
|
215 nsSVGOuterSVGFrame *outerSVGFrame = nullptr; |
|
216 |
|
217 // We must not add dirty bits to the nsSVGOuterSVGFrame or else |
|
218 // PresShell::FrameNeedsReflow won't work when we pass it in below. |
|
219 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) { |
|
220 outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(aFrame); |
|
221 } else { |
|
222 aFrame->AddStateBits(NS_FRAME_IS_DIRTY); |
|
223 |
|
224 nsIFrame *f = aFrame->GetParent(); |
|
225 while (f && !(f->GetStateBits() & NS_STATE_IS_OUTER_SVG)) { |
|
226 if (f->GetStateBits() & |
|
227 (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) { |
|
228 return; |
|
229 } |
|
230 f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); |
|
231 f = f->GetParent(); |
|
232 NS_ABORT_IF_FALSE(f->IsFrameOfType(nsIFrame::eSVG), |
|
233 "NS_STATE_IS_OUTER_SVG check above not valid!"); |
|
234 } |
|
235 |
|
236 outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(f); |
|
237 |
|
238 NS_ABORT_IF_FALSE(outerSVGFrame && |
|
239 outerSVGFrame->GetType() == nsGkAtoms::svgOuterSVGFrame, |
|
240 "Did not find nsSVGOuterSVGFrame!"); |
|
241 } |
|
242 |
|
243 if (outerSVGFrame->GetStateBits() & NS_FRAME_IN_REFLOW) { |
|
244 // We're currently under an nsSVGOuterSVGFrame::Reflow call so there is no |
|
245 // need to call PresShell::FrameNeedsReflow, since we have an |
|
246 // nsSVGOuterSVGFrame::DidReflow call pending. |
|
247 return; |
|
248 } |
|
249 |
|
250 nsFrameState dirtyBit = |
|
251 (outerSVGFrame == aFrame ? NS_FRAME_IS_DIRTY : NS_FRAME_HAS_DIRTY_CHILDREN); |
|
252 |
|
253 aFrame->PresContext()->PresShell()->FrameNeedsReflow( |
|
254 outerSVGFrame, nsIPresShell::eResize, dirtyBit); |
|
255 } |
|
256 |
|
257 bool |
|
258 nsSVGUtils::NeedsReflowSVG(nsIFrame *aFrame) |
|
259 { |
|
260 NS_ABORT_IF_FALSE(aFrame->IsFrameOfType(nsIFrame::eSVG), |
|
261 "SVG uses bits differently!"); |
|
262 |
|
263 // The flags we test here may change, hence why we have this separate |
|
264 // function. |
|
265 return NS_SUBTREE_DIRTY(aFrame); |
|
266 } |
|
267 |
|
268 void |
|
269 nsSVGUtils::NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame) |
|
270 { |
|
271 NS_ABORT_IF_FALSE(!(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG), |
|
272 "Not expecting to be called on the outer SVG Frame"); |
|
273 |
|
274 aFrame = aFrame->GetParent(); |
|
275 |
|
276 while (aFrame) { |
|
277 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) |
|
278 return; |
|
279 |
|
280 nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame); |
|
281 if (property) { |
|
282 property->Invalidate(); |
|
283 } |
|
284 aFrame = aFrame->GetParent(); |
|
285 } |
|
286 } |
|
287 |
|
288 float |
|
289 nsSVGUtils::ObjectSpace(const gfxRect &aRect, const nsSVGLength2 *aLength) |
|
290 { |
|
291 float axis; |
|
292 |
|
293 switch (aLength->GetCtxType()) { |
|
294 case SVGContentUtils::X: |
|
295 axis = aRect.Width(); |
|
296 break; |
|
297 case SVGContentUtils::Y: |
|
298 axis = aRect.Height(); |
|
299 break; |
|
300 case SVGContentUtils::XY: |
|
301 axis = float(SVGContentUtils::ComputeNormalizedHypotenuse( |
|
302 aRect.Width(), aRect.Height())); |
|
303 break; |
|
304 default: |
|
305 NS_NOTREACHED("unexpected ctx type"); |
|
306 axis = 0.0f; |
|
307 break; |
|
308 } |
|
309 if (aLength->IsPercentage()) { |
|
310 // Multiply first to avoid precision errors: |
|
311 return axis * aLength->GetAnimValInSpecifiedUnits() / 100; |
|
312 } |
|
313 return aLength->GetAnimValue(static_cast<SVGSVGElement*>(nullptr)) * axis; |
|
314 } |
|
315 |
|
316 float |
|
317 nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength) |
|
318 { |
|
319 return aLength->GetAnimValue(aSVGElement); |
|
320 } |
|
321 |
|
322 float |
|
323 nsSVGUtils::UserSpace(nsIFrame *aNonSVGContext, const nsSVGLength2 *aLength) |
|
324 { |
|
325 return aLength->GetAnimValue(aNonSVGContext); |
|
326 } |
|
327 |
|
328 nsSVGOuterSVGFrame * |
|
329 nsSVGUtils::GetOuterSVGFrame(nsIFrame *aFrame) |
|
330 { |
|
331 while (aFrame) { |
|
332 if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) { |
|
333 return static_cast<nsSVGOuterSVGFrame*>(aFrame); |
|
334 } |
|
335 aFrame = aFrame->GetParent(); |
|
336 } |
|
337 |
|
338 return nullptr; |
|
339 } |
|
340 |
|
341 nsIFrame* |
|
342 nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect) |
|
343 { |
|
344 nsISVGChildFrame* svg = do_QueryFrame(aFrame); |
|
345 if (!svg) |
|
346 return nullptr; |
|
347 *aRect = (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ? |
|
348 nsRect(0, 0, 0, 0) : svg->GetCoveredRegion(); |
|
349 return GetOuterSVGFrame(aFrame); |
|
350 } |
|
351 |
|
352 gfxMatrix |
|
353 nsSVGUtils::GetCanvasTM(nsIFrame *aFrame, uint32_t aFor, |
|
354 nsIFrame* aTransformRoot) |
|
355 { |
|
356 // XXX yuck, we really need a common interface for GetCanvasTM |
|
357 |
|
358 if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) { |
|
359 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame); |
|
360 } |
|
361 |
|
362 if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) && |
|
363 !aTransformRoot) { |
|
364 if ((aFor == nsISVGChildFrame::FOR_PAINTING && |
|
365 NS_SVGDisplayListPaintingEnabled()) || |
|
366 (aFor == nsISVGChildFrame::FOR_HIT_TESTING && |
|
367 NS_SVGDisplayListHitTestingEnabled())) { |
|
368 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame); |
|
369 } |
|
370 } |
|
371 |
|
372 nsIAtom* type = aFrame->GetType(); |
|
373 if (type == nsGkAtoms::svgForeignObjectFrame) { |
|
374 return static_cast<nsSVGForeignObjectFrame*>(aFrame)-> |
|
375 GetCanvasTM(aFor, aTransformRoot); |
|
376 } |
|
377 if (type == nsGkAtoms::svgOuterSVGFrame) { |
|
378 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame); |
|
379 } |
|
380 |
|
381 nsSVGContainerFrame *containerFrame = do_QueryFrame(aFrame); |
|
382 if (containerFrame) { |
|
383 return containerFrame->GetCanvasTM(aFor, aTransformRoot); |
|
384 } |
|
385 |
|
386 return static_cast<nsSVGPathGeometryFrame*>(aFrame)-> |
|
387 GetCanvasTM(aFor, aTransformRoot); |
|
388 } |
|
389 |
|
390 gfxMatrix |
|
391 nsSVGUtils::GetUserToCanvasTM(nsIFrame *aFrame, uint32_t aFor) |
|
392 { |
|
393 NS_ASSERTION(aFor == nsISVGChildFrame::FOR_OUTERSVG_TM, |
|
394 "Unexpected aFor?"); |
|
395 |
|
396 nsISVGChildFrame* svgFrame = do_QueryFrame(aFrame); |
|
397 NS_ASSERTION(svgFrame, "bad frame"); |
|
398 |
|
399 gfxMatrix tm; |
|
400 if (svgFrame) { |
|
401 nsSVGElement *content = static_cast<nsSVGElement*>(aFrame->GetContent()); |
|
402 tm = content->PrependLocalTransformsTo( |
|
403 GetCanvasTM(aFrame->GetParent(), aFor), |
|
404 nsSVGElement::eUserSpaceToParent); |
|
405 } |
|
406 return tm; |
|
407 } |
|
408 |
|
409 void |
|
410 nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame *aFrame, uint32_t aFlags) |
|
411 { |
|
412 nsIFrame *kid = aFrame->GetFirstPrincipalChild(); |
|
413 |
|
414 while (kid) { |
|
415 nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); |
|
416 if (SVGFrame) { |
|
417 SVGFrame->NotifySVGChanged(aFlags); |
|
418 } else { |
|
419 NS_ASSERTION(kid->IsFrameOfType(nsIFrame::eSVG) || kid->IsSVGText(), |
|
420 "SVG frame expected"); |
|
421 // recurse into the children of container frames e.g. <clipPath>, <mask> |
|
422 // in case they have child frames with transformation matrices |
|
423 if (kid->IsFrameOfType(nsIFrame::eSVG)) { |
|
424 NotifyChildrenOfSVGChange(kid, aFlags); |
|
425 } |
|
426 } |
|
427 kid = kid->GetNextSibling(); |
|
428 } |
|
429 } |
|
430 |
|
431 // ************************************************************ |
|
432 |
|
433 class SVGPaintCallback : public nsSVGFilterPaintCallback |
|
434 { |
|
435 public: |
|
436 virtual void Paint(nsRenderingContext *aContext, nsIFrame *aTarget, |
|
437 const nsIntRect* aDirtyRect, |
|
438 nsIFrame* aTransformRoot) MOZ_OVERRIDE |
|
439 { |
|
440 nsISVGChildFrame *svgChildFrame = do_QueryFrame(aTarget); |
|
441 NS_ASSERTION(svgChildFrame, "Expected SVG frame here"); |
|
442 |
|
443 nsIntRect* dirtyRect = nullptr; |
|
444 nsIntRect tmpDirtyRect; |
|
445 |
|
446 // aDirtyRect is in user-space pixels, we need to convert to |
|
447 // outer-SVG-frame-relative device pixels. |
|
448 if (aDirtyRect) { |
|
449 gfxMatrix userToDeviceSpace = |
|
450 nsSVGUtils::GetCanvasTM(aTarget, nsISVGChildFrame::FOR_PAINTING, aTransformRoot); |
|
451 if (userToDeviceSpace.IsSingular()) { |
|
452 return; |
|
453 } |
|
454 gfxRect dirtyBounds = userToDeviceSpace.TransformBounds( |
|
455 gfxRect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height)); |
|
456 dirtyBounds.RoundOut(); |
|
457 if (gfxUtils::GfxRectToIntRect(dirtyBounds, &tmpDirtyRect)) { |
|
458 dirtyRect = &tmpDirtyRect; |
|
459 } |
|
460 } |
|
461 |
|
462 svgChildFrame->PaintSVG(aContext, dirtyRect, aTransformRoot); |
|
463 } |
|
464 }; |
|
465 |
|
466 void |
|
467 nsSVGUtils::PaintFrameWithEffects(nsRenderingContext *aContext, |
|
468 const nsIntRect *aDirtyRect, |
|
469 nsIFrame *aFrame, |
|
470 nsIFrame *aTransformRoot) |
|
471 { |
|
472 NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || |
|
473 (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) || |
|
474 aFrame->PresContext()->IsGlyph(), |
|
475 "If display lists are enabled, only painting of non-display " |
|
476 "SVG should take this code path"); |
|
477 |
|
478 nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame); |
|
479 if (!svgChildFrame) |
|
480 return; |
|
481 |
|
482 float opacity = aFrame->StyleDisplay()->mOpacity; |
|
483 if (opacity == 0.0f) |
|
484 return; |
|
485 |
|
486 const nsIContent* content = aFrame->GetContent(); |
|
487 if (content->IsSVG() && |
|
488 !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) { |
|
489 return; |
|
490 } |
|
491 |
|
492 /* Properties are added lazily and may have been removed by a restyle, |
|
493 so make sure all applicable ones are set again. */ |
|
494 |
|
495 nsSVGEffects::EffectProperties effectProperties = |
|
496 nsSVGEffects::GetEffectProperties(aFrame); |
|
497 |
|
498 bool isOK = effectProperties.HasNoFilterOrHasValidFilter(); |
|
499 |
|
500 if (aDirtyRect && |
|
501 !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) { |
|
502 // Here we convert aFrame's paint bounds to outer-<svg> device space, |
|
503 // compare it to aDirtyRect, and return early if they don't intersect. |
|
504 // We don't do this optimization for nondisplay SVG since nondisplay |
|
505 // SVG doesn't maintain bounds/overflow rects. |
|
506 nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf(); |
|
507 if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) || |
|
508 aFrame->IsSVGText()) { |
|
509 // Unlike containers, leaf frames do not include GetPosition() in |
|
510 // GetCanvasTM(). |
|
511 overflowRect = overflowRect + aFrame->GetPosition(); |
|
512 } |
|
513 int32_t appUnitsPerDevPx = aFrame->PresContext()->AppUnitsPerDevPixel(); |
|
514 gfxMatrix tm = GetCanvasTM(aFrame, nsISVGChildFrame::FOR_PAINTING, aTransformRoot); |
|
515 if (aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) { |
|
516 gfx::Matrix childrenOnlyTM; |
|
517 if (static_cast<nsSVGContainerFrame*>(aFrame)-> |
|
518 HasChildrenOnlyTransform(&childrenOnlyTM)) { |
|
519 // Undo the children-only transform: |
|
520 tm = ThebesMatrix(childrenOnlyTM).Invert() * tm; |
|
521 } |
|
522 } |
|
523 nsIntRect bounds = TransformFrameRectToOuterSVG(overflowRect, |
|
524 tm, aFrame->PresContext()). |
|
525 ToOutsidePixels(appUnitsPerDevPx); |
|
526 if (!aDirtyRect->Intersects(bounds)) { |
|
527 return; |
|
528 } |
|
529 } |
|
530 |
|
531 /* SVG defines the following rendering model: |
|
532 * |
|
533 * 1. Render fill |
|
534 * 2. Render stroke |
|
535 * 3. Render markers |
|
536 * 4. Apply filter |
|
537 * 5. Apply clipping, masking, group opacity |
|
538 * |
|
539 * We follow this, but perform a couple of optimizations: |
|
540 * |
|
541 * + Use cairo's clipPath when representable natively (single object |
|
542 * clip region). |
|
543 * |
|
544 * + Merge opacity and masking if both used together. |
|
545 */ |
|
546 |
|
547 if (opacity != 1.0f && CanOptimizeOpacity(aFrame)) |
|
548 opacity = 1.0f; |
|
549 |
|
550 gfxContext *gfx = aContext->ThebesContext(); |
|
551 bool complexEffects = false; |
|
552 |
|
553 nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK); |
|
554 nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK); |
|
555 |
|
556 bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true; |
|
557 |
|
558 if (!isOK) { |
|
559 // Some resource is invalid. We shouldn't paint anything. |
|
560 return; |
|
561 } |
|
562 |
|
563 gfxMatrix matrix; |
|
564 if (clipPathFrame || maskFrame) |
|
565 matrix = GetCanvasTM(aFrame, nsISVGChildFrame::FOR_PAINTING, aTransformRoot); |
|
566 |
|
567 /* Check if we need to do additional operations on this child's |
|
568 * rendering, which necessitates rendering into another surface. */ |
|
569 if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip) |
|
570 || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { |
|
571 complexEffects = true; |
|
572 gfx->Save(); |
|
573 if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) { |
|
574 // aFrame has a valid visual overflow rect, so clip to it before calling |
|
575 // PushGroup() to minimize the size of the surfaces we'll composite: |
|
576 gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(gfx); |
|
577 gfx->Multiply(GetCanvasTM(aFrame, nsISVGChildFrame::FOR_PAINTING, aTransformRoot)); |
|
578 nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf(); |
|
579 if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) || |
|
580 aFrame->IsSVGText()) { |
|
581 // Unlike containers, leaf frames do not include GetPosition() in |
|
582 // GetCanvasTM(). |
|
583 overflowRect = overflowRect + aFrame->GetPosition(); |
|
584 } |
|
585 aContext->IntersectClip(overflowRect); |
|
586 } |
|
587 gfx->PushGroup(gfxContentType::COLOR_ALPHA); |
|
588 } |
|
589 |
|
590 /* If this frame has only a trivial clipPath, set up cairo's clipping now so |
|
591 * we can just do normal painting and get it clipped appropriately. |
|
592 */ |
|
593 if (clipPathFrame && isTrivialClip) { |
|
594 gfx->Save(); |
|
595 clipPathFrame->ClipPaint(aContext, aFrame, matrix); |
|
596 } |
|
597 |
|
598 /* Paint the child */ |
|
599 if (effectProperties.HasValidFilter()) { |
|
600 nsRegion* dirtyRegion = nullptr; |
|
601 nsRegion tmpDirtyRegion; |
|
602 if (aDirtyRect) { |
|
603 // aDirtyRect is in outer-<svg> device pixels, but the filter code needs |
|
604 // it in frame space. |
|
605 gfxMatrix userToDeviceSpace = |
|
606 GetUserToCanvasTM(aFrame, nsISVGChildFrame::FOR_OUTERSVG_TM); |
|
607 if (userToDeviceSpace.IsSingular()) { |
|
608 return; |
|
609 } |
|
610 gfxMatrix deviceToUserSpace = userToDeviceSpace; |
|
611 deviceToUserSpace.Invert(); |
|
612 gfxRect dirtyBounds = deviceToUserSpace.TransformBounds( |
|
613 gfxRect(aDirtyRect->x, aDirtyRect->y, |
|
614 aDirtyRect->width, aDirtyRect->height)); |
|
615 tmpDirtyRegion = |
|
616 nsLayoutUtils::RoundGfxRectToAppRect( |
|
617 dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) - |
|
618 aFrame->GetPosition(); |
|
619 dirtyRegion = &tmpDirtyRegion; |
|
620 } |
|
621 SVGPaintCallback paintCallback; |
|
622 nsFilterInstance::PaintFilteredFrame(aContext, aFrame, &paintCallback, |
|
623 dirtyRegion, aTransformRoot); |
|
624 } else { |
|
625 svgChildFrame->PaintSVG(aContext, aDirtyRect, aTransformRoot); |
|
626 } |
|
627 |
|
628 if (clipPathFrame && isTrivialClip) { |
|
629 gfx->Restore(); |
|
630 } |
|
631 |
|
632 /* No more effects, we're done. */ |
|
633 if (!complexEffects) |
|
634 return; |
|
635 |
|
636 gfx->PopGroupToSource(); |
|
637 |
|
638 nsRefPtr<gfxPattern> maskSurface = |
|
639 maskFrame ? maskFrame->ComputeMaskAlpha(aContext, aFrame, |
|
640 matrix, opacity) : nullptr; |
|
641 |
|
642 nsRefPtr<gfxPattern> clipMaskSurface; |
|
643 if (clipPathFrame && !isTrivialClip) { |
|
644 gfx->PushGroup(gfxContentType::COLOR_ALPHA); |
|
645 |
|
646 nsresult rv = clipPathFrame->ClipPaint(aContext, aFrame, matrix); |
|
647 clipMaskSurface = gfx->PopGroup(); |
|
648 |
|
649 if (NS_SUCCEEDED(rv) && clipMaskSurface) { |
|
650 // Still more set after clipping, so clip to another surface |
|
651 if (maskSurface || opacity != 1.0f) { |
|
652 gfx->PushGroup(gfxContentType::COLOR_ALPHA); |
|
653 gfx->Mask(clipMaskSurface); |
|
654 gfx->PopGroupToSource(); |
|
655 } else { |
|
656 gfx->Mask(clipMaskSurface); |
|
657 } |
|
658 } |
|
659 } |
|
660 |
|
661 if (maskSurface) { |
|
662 gfx->Mask(maskSurface); |
|
663 } else if (opacity != 1.0f) { |
|
664 gfx->Paint(opacity); |
|
665 } |
|
666 |
|
667 gfx->Restore(); |
|
668 } |
|
669 |
|
670 bool |
|
671 nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint) |
|
672 { |
|
673 nsSVGEffects::EffectProperties props = |
|
674 nsSVGEffects::GetEffectProperties(aFrame); |
|
675 if (!props.mClipPath) |
|
676 return true; |
|
677 |
|
678 bool isOK = true; |
|
679 nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame(&isOK); |
|
680 if (!isOK) { |
|
681 // clipPath is not a valid resource, so nothing gets painted, so |
|
682 // hit-testing must fail. |
|
683 return false; |
|
684 } |
|
685 if (!clipPathFrame) { |
|
686 // clipPath doesn't exist, ignore it. |
|
687 return true; |
|
688 } |
|
689 |
|
690 return clipPathFrame->ClipHitTest(aFrame, GetCanvasTM(aFrame, |
|
691 nsISVGChildFrame::FOR_HIT_TESTING), aPoint); |
|
692 } |
|
693 |
|
694 nsIFrame * |
|
695 nsSVGUtils::HitTestChildren(nsIFrame *aFrame, const nsPoint &aPoint) |
|
696 { |
|
697 // Traverse the list in reverse order, so that if we get a hit we know that's |
|
698 // the topmost frame that intersects the point; then we can just return it. |
|
699 nsIFrame* result = nullptr; |
|
700 for (nsIFrame* current = aFrame->PrincipalChildList().LastChild(); |
|
701 current; |
|
702 current = current->GetPrevSibling()) { |
|
703 nsISVGChildFrame* SVGFrame = do_QueryFrame(current); |
|
704 if (SVGFrame) { |
|
705 const nsIContent* content = current->GetContent(); |
|
706 if (content->IsSVG() && |
|
707 !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) { |
|
708 continue; |
|
709 } |
|
710 result = SVGFrame->GetFrameForPoint(aPoint); |
|
711 if (result) |
|
712 break; |
|
713 } |
|
714 } |
|
715 |
|
716 if (result && !HitTestClip(aFrame, aPoint)) |
|
717 result = nullptr; |
|
718 |
|
719 return result; |
|
720 } |
|
721 |
|
722 nsRect |
|
723 nsSVGUtils::GetCoveredRegion(const nsFrameList &aFrames) |
|
724 { |
|
725 nsRect rect; |
|
726 |
|
727 for (nsIFrame* kid = aFrames.FirstChild(); |
|
728 kid; |
|
729 kid = kid->GetNextSibling()) { |
|
730 nsISVGChildFrame* child = do_QueryFrame(kid); |
|
731 if (child) { |
|
732 nsRect childRect = child->GetCoveredRegion(); |
|
733 rect.UnionRect(rect, childRect); |
|
734 } |
|
735 } |
|
736 |
|
737 return rect; |
|
738 } |
|
739 |
|
740 nsPoint |
|
741 nsSVGUtils::TransformOuterSVGPointToChildFrame(nsPoint aPoint, |
|
742 const gfxMatrix& aFrameToCanvasTM, |
|
743 nsPresContext* aPresContext) |
|
744 { |
|
745 NS_ABORT_IF_FALSE(!aFrameToCanvasTM.IsSingular(), |
|
746 "Callers must not pass a singular matrix"); |
|
747 gfxMatrix canvasDevToFrameUserSpace = aFrameToCanvasTM; |
|
748 canvasDevToFrameUserSpace.Invert(); |
|
749 gfxPoint devPt = gfxPoint(aPoint.x, aPoint.y) / |
|
750 aPresContext->AppUnitsPerDevPixel(); |
|
751 gfxPoint userPt = canvasDevToFrameUserSpace.Transform(devPt); |
|
752 gfxPoint appPt = (userPt * aPresContext->AppUnitsPerCSSPixel()).Round(); |
|
753 userPt.x = clamped(appPt.x, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX)); |
|
754 userPt.y = clamped(appPt.y, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX)); |
|
755 // now guaranteed to be safe: |
|
756 return nsPoint(nscoord(userPt.x), nscoord(userPt.y)); |
|
757 } |
|
758 |
|
759 nsRect |
|
760 nsSVGUtils::TransformFrameRectToOuterSVG(const nsRect& aRect, |
|
761 const gfxMatrix& aMatrix, |
|
762 nsPresContext* aPresContext) |
|
763 { |
|
764 gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height); |
|
765 r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel()); |
|
766 return nsLayoutUtils::RoundGfxRectToAppRect( |
|
767 aMatrix.TransformBounds(r), aPresContext->AppUnitsPerDevPixel()); |
|
768 } |
|
769 |
|
770 gfxIntSize |
|
771 nsSVGUtils::ConvertToSurfaceSize(const gfxSize& aSize, |
|
772 bool *aResultOverflows) |
|
773 { |
|
774 gfxIntSize surfaceSize(ClampToInt(ceil(aSize.width)), ClampToInt(ceil(aSize.height))); |
|
775 |
|
776 *aResultOverflows = surfaceSize.width != ceil(aSize.width) || |
|
777 surfaceSize.height != ceil(aSize.height); |
|
778 |
|
779 if (!gfxASurface::CheckSurfaceSize(surfaceSize)) { |
|
780 surfaceSize.width = std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION, |
|
781 surfaceSize.width); |
|
782 surfaceSize.height = std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION, |
|
783 surfaceSize.height); |
|
784 *aResultOverflows = true; |
|
785 } |
|
786 |
|
787 return surfaceSize; |
|
788 } |
|
789 |
|
790 bool |
|
791 nsSVGUtils::HitTestRect(const gfx::Matrix &aMatrix, |
|
792 float aRX, float aRY, float aRWidth, float aRHeight, |
|
793 float aX, float aY) |
|
794 { |
|
795 gfx::Rect rect(aRX, aRY, aRWidth, aRHeight); |
|
796 if (rect.IsEmpty() || aMatrix.IsSingular()) { |
|
797 return false; |
|
798 } |
|
799 gfx::Matrix toRectSpace = aMatrix; |
|
800 toRectSpace.Invert(); |
|
801 gfx::Point p = toRectSpace * gfx::Point(aX, aY); |
|
802 return rect.x <= p.x && p.x <= rect.XMost() && |
|
803 rect.y <= p.y && p.y <= rect.YMost(); |
|
804 } |
|
805 |
|
806 gfxRect |
|
807 nsSVGUtils::GetClipRectForFrame(nsIFrame *aFrame, |
|
808 float aX, float aY, float aWidth, float aHeight) |
|
809 { |
|
810 const nsStyleDisplay* disp = aFrame->StyleDisplay(); |
|
811 |
|
812 if (!(disp->mClipFlags & NS_STYLE_CLIP_RECT)) { |
|
813 NS_ASSERTION(disp->mClipFlags == NS_STYLE_CLIP_AUTO, |
|
814 "We don't know about this type of clip."); |
|
815 return gfxRect(aX, aY, aWidth, aHeight); |
|
816 } |
|
817 |
|
818 if (disp->mOverflowX == NS_STYLE_OVERFLOW_HIDDEN || |
|
819 disp->mOverflowY == NS_STYLE_OVERFLOW_HIDDEN) { |
|
820 |
|
821 nsIntRect clipPxRect = |
|
822 disp->mClip.ToOutsidePixels(aFrame->PresContext()->AppUnitsPerDevPixel()); |
|
823 gfxRect clipRect = |
|
824 gfxRect(clipPxRect.x, clipPxRect.y, clipPxRect.width, clipPxRect.height); |
|
825 |
|
826 if (NS_STYLE_CLIP_RIGHT_AUTO & disp->mClipFlags) { |
|
827 clipRect.width = aWidth - clipRect.X(); |
|
828 } |
|
829 if (NS_STYLE_CLIP_BOTTOM_AUTO & disp->mClipFlags) { |
|
830 clipRect.height = aHeight - clipRect.Y(); |
|
831 } |
|
832 |
|
833 if (disp->mOverflowX != NS_STYLE_OVERFLOW_HIDDEN) { |
|
834 clipRect.x = aX; |
|
835 clipRect.width = aWidth; |
|
836 } |
|
837 if (disp->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN) { |
|
838 clipRect.y = aY; |
|
839 clipRect.height = aHeight; |
|
840 } |
|
841 |
|
842 return clipRect; |
|
843 } |
|
844 return gfxRect(aX, aY, aWidth, aHeight); |
|
845 } |
|
846 |
|
847 void |
|
848 nsSVGUtils::SetClipRect(gfxContext *aContext, |
|
849 const gfxMatrix &aCTM, |
|
850 const gfxRect &aRect) |
|
851 { |
|
852 if (aCTM.IsSingular()) |
|
853 return; |
|
854 |
|
855 gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(aContext); |
|
856 aContext->Multiply(aCTM); |
|
857 aContext->Clip(aRect); |
|
858 } |
|
859 |
|
860 gfxRect |
|
861 nsSVGUtils::GetBBox(nsIFrame *aFrame, uint32_t aFlags) |
|
862 { |
|
863 if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) { |
|
864 aFrame = aFrame->GetParent(); |
|
865 } |
|
866 gfxRect bbox; |
|
867 nsISVGChildFrame *svg = do_QueryFrame(aFrame); |
|
868 if (svg || aFrame->IsSVGText()) { |
|
869 // It is possible to apply a gradient, pattern, clipping path, mask or |
|
870 // filter to text. When one of these facilities is applied to text |
|
871 // the bounding box is the entire text element in all |
|
872 // cases. |
|
873 if (aFrame->IsSVGText()) { |
|
874 nsIFrame* ancestor = GetFirstNonAAncestorFrame(aFrame); |
|
875 if (ancestor && ancestor->IsSVGText()) { |
|
876 while (ancestor->GetType() != nsGkAtoms::svgTextFrame) { |
|
877 ancestor = ancestor->GetParent(); |
|
878 } |
|
879 } |
|
880 svg = do_QueryFrame(ancestor); |
|
881 } |
|
882 nsIContent* content = aFrame->GetContent(); |
|
883 if (content->IsSVG() && |
|
884 !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) { |
|
885 return bbox; |
|
886 } |
|
887 gfxMatrix matrix; |
|
888 if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) { |
|
889 // The spec says getBBox "Returns the tight bounding box in *current user |
|
890 // space*". So we should really be doing this for all elements, but that |
|
891 // needs investigation to check that we won't break too much content. |
|
892 // NOTE: When changing this to apply to other frame types, make sure to |
|
893 // also update nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset. |
|
894 NS_ABORT_IF_FALSE(content->IsSVG(), "bad cast"); |
|
895 nsSVGElement *element = static_cast<nsSVGElement*>(content); |
|
896 matrix = element->PrependLocalTransformsTo(matrix, |
|
897 nsSVGElement::eChildToUserSpace); |
|
898 } |
|
899 return svg->GetBBoxContribution(ToMatrix(matrix), aFlags).ToThebesRect(); |
|
900 } |
|
901 return nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame); |
|
902 } |
|
903 |
|
904 gfxPoint |
|
905 nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame *aFrame) |
|
906 { |
|
907 if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) { |
|
908 // The user space for non-SVG frames is defined as the bounding box of the |
|
909 // frame's border-box rects over all continuations. |
|
910 return gfxPoint(); |
|
911 } |
|
912 |
|
913 // Leaf frames apply their own offset inside their user space. |
|
914 if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) || |
|
915 aFrame->IsSVGText()) { |
|
916 return nsLayoutUtils::RectToGfxRect(aFrame->GetRect(), |
|
917 nsPresContext::AppUnitsPerCSSPixel()).TopLeft(); |
|
918 } |
|
919 |
|
920 // For foreignObject frames, nsSVGUtils::GetBBox applies their local |
|
921 // transform, so we need to do the same here. |
|
922 if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) { |
|
923 gfxMatrix transform = static_cast<nsSVGElement*>(aFrame->GetContent())-> |
|
924 PrependLocalTransformsTo(gfxMatrix(), |
|
925 nsSVGElement::eChildToUserSpace); |
|
926 NS_ASSERTION(!transform.HasNonTranslation(), "we're relying on this being an offset-only transform"); |
|
927 return transform.GetTranslation(); |
|
928 } |
|
929 |
|
930 return gfxPoint(); |
|
931 } |
|
932 |
|
933 gfxRect |
|
934 nsSVGUtils::GetRelativeRect(uint16_t aUnits, const nsSVGLength2 *aXYWH, |
|
935 const gfxRect &aBBox, nsIFrame *aFrame) |
|
936 { |
|
937 float x, y, width, height; |
|
938 if (aUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { |
|
939 x = aBBox.X() + ObjectSpace(aBBox, &aXYWH[0]); |
|
940 y = aBBox.Y() + ObjectSpace(aBBox, &aXYWH[1]); |
|
941 width = ObjectSpace(aBBox, &aXYWH[2]); |
|
942 height = ObjectSpace(aBBox, &aXYWH[3]); |
|
943 } else { |
|
944 x = UserSpace(aFrame, &aXYWH[0]); |
|
945 y = UserSpace(aFrame, &aXYWH[1]); |
|
946 width = UserSpace(aFrame, &aXYWH[2]); |
|
947 height = UserSpace(aFrame, &aXYWH[3]); |
|
948 } |
|
949 return gfxRect(x, y, width, height); |
|
950 } |
|
951 |
|
952 bool |
|
953 nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame) |
|
954 { |
|
955 if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) { |
|
956 return false; |
|
957 } |
|
958 nsIAtom *type = aFrame->GetType(); |
|
959 if (type != nsGkAtoms::svgImageFrame && |
|
960 type != nsGkAtoms::svgPathGeometryFrame) { |
|
961 return false; |
|
962 } |
|
963 if (aFrame->StyleSVGReset()->HasFilters()) { |
|
964 return false; |
|
965 } |
|
966 // XXX The SVG WG is intending to allow fill, stroke and markers on <image> |
|
967 if (type == nsGkAtoms::svgImageFrame) { |
|
968 return true; |
|
969 } |
|
970 const nsStyleSVG *style = aFrame->StyleSVG(); |
|
971 if (style->HasMarker()) { |
|
972 return false; |
|
973 } |
|
974 if (!style->HasFill() || !HasStroke(aFrame)) { |
|
975 return true; |
|
976 } |
|
977 return false; |
|
978 } |
|
979 |
|
980 gfxMatrix |
|
981 nsSVGUtils::AdjustMatrixForUnits(const gfxMatrix &aMatrix, |
|
982 nsSVGEnum *aUnits, |
|
983 nsIFrame *aFrame) |
|
984 { |
|
985 if (aFrame && |
|
986 aUnits->GetAnimValue() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { |
|
987 gfxRect bbox = GetBBox(aFrame); |
|
988 return gfxMatrix().Scale(bbox.Width(), bbox.Height()) * |
|
989 gfxMatrix().Translate(gfxPoint(bbox.X(), bbox.Y())) * |
|
990 aMatrix; |
|
991 } |
|
992 return aMatrix; |
|
993 } |
|
994 |
|
995 nsIFrame* |
|
996 nsSVGUtils::GetFirstNonAAncestorFrame(nsIFrame* aStartFrame) |
|
997 { |
|
998 for (nsIFrame *ancestorFrame = aStartFrame; ancestorFrame; |
|
999 ancestorFrame = ancestorFrame->GetParent()) { |
|
1000 if (ancestorFrame->GetType() != nsGkAtoms::svgAFrame) { |
|
1001 return ancestorFrame; |
|
1002 } |
|
1003 } |
|
1004 return nullptr; |
|
1005 } |
|
1006 |
|
1007 gfxMatrix |
|
1008 nsSVGUtils::GetStrokeTransform(nsIFrame *aFrame) |
|
1009 { |
|
1010 if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) { |
|
1011 aFrame = aFrame->GetParent(); |
|
1012 } |
|
1013 |
|
1014 if (aFrame->StyleSVGReset()->mVectorEffect == |
|
1015 NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) { |
|
1016 |
|
1017 nsIContent *content = aFrame->GetContent(); |
|
1018 NS_ABORT_IF_FALSE(content->IsSVG(), "bad cast"); |
|
1019 |
|
1020 // a non-scaling stroke is in the screen co-ordinate |
|
1021 // space rather so we need to invert the transform |
|
1022 // to the screen co-ordinate space to get there. |
|
1023 // See http://www.w3.org/TR/SVGTiny12/painting.html#NonScalingStroke |
|
1024 gfx::Matrix transform = SVGContentUtils::GetCTM( |
|
1025 static_cast<nsSVGElement*>(content), true); |
|
1026 if (!transform.IsSingular()) { |
|
1027 transform.Invert(); |
|
1028 return ThebesMatrix(transform); |
|
1029 } |
|
1030 } |
|
1031 return gfxMatrix(); |
|
1032 } |
|
1033 |
|
1034 // The logic here comes from _cairo_stroke_style_max_distance_from_path |
|
1035 static gfxRect |
|
1036 PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents, |
|
1037 nsIFrame* aFrame, |
|
1038 double aStyleExpansionFactor, |
|
1039 const gfxMatrix& aMatrix) |
|
1040 { |
|
1041 double style_expansion = |
|
1042 aStyleExpansionFactor * nsSVGUtils::GetStrokeWidth(aFrame); |
|
1043 |
|
1044 gfxMatrix matrix = aMatrix; |
|
1045 matrix.Multiply(nsSVGUtils::GetStrokeTransform(aFrame)); |
|
1046 |
|
1047 double dx = style_expansion * (fabs(matrix.xx) + fabs(matrix.xy)); |
|
1048 double dy = style_expansion * (fabs(matrix.yy) + fabs(matrix.yx)); |
|
1049 |
|
1050 gfxRect strokeExtents = aPathExtents; |
|
1051 strokeExtents.Inflate(dx, dy); |
|
1052 return strokeExtents; |
|
1053 } |
|
1054 |
|
1055 /*static*/ gfxRect |
|
1056 nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents, |
|
1057 nsTextFrame* aFrame, |
|
1058 const gfxMatrix& aMatrix) |
|
1059 { |
|
1060 NS_ASSERTION(aFrame->IsSVGText(), "expected an nsTextFrame for SVG text"); |
|
1061 return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame, 0.5, aMatrix); |
|
1062 } |
|
1063 |
|
1064 /*static*/ gfxRect |
|
1065 nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents, |
|
1066 nsSVGPathGeometryFrame* aFrame, |
|
1067 const gfxMatrix& aMatrix) |
|
1068 { |
|
1069 double styleExpansionFactor = 0.5; |
|
1070 |
|
1071 if (static_cast<nsSVGPathGeometryElement*>(aFrame->GetContent())->IsMarkable()) { |
|
1072 const nsStyleSVG* style = aFrame->StyleSVG(); |
|
1073 |
|
1074 if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) { |
|
1075 styleExpansionFactor = M_SQRT1_2; |
|
1076 } |
|
1077 |
|
1078 if (style->mStrokeLinejoin == NS_STYLE_STROKE_LINEJOIN_MITER && |
|
1079 styleExpansionFactor < style->mStrokeMiterlimit && |
|
1080 aFrame->GetContent()->Tag() != nsGkAtoms::line) { |
|
1081 styleExpansionFactor = style->mStrokeMiterlimit; |
|
1082 } |
|
1083 } |
|
1084 |
|
1085 return ::PathExtentsToMaxStrokeExtents(aPathExtents, |
|
1086 aFrame, |
|
1087 styleExpansionFactor, |
|
1088 aMatrix); |
|
1089 } |
|
1090 |
|
1091 // ---------------------------------------------------------------------- |
|
1092 |
|
1093 /* static */ nscolor |
|
1094 nsSVGUtils::GetFallbackOrPaintColor(gfxContext *aContext, nsStyleContext *aStyleContext, |
|
1095 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke) |
|
1096 { |
|
1097 const nsStyleSVGPaint &paint = aStyleContext->StyleSVG()->*aFillOrStroke; |
|
1098 nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited(); |
|
1099 bool isServer = paint.mType == eStyleSVGPaintType_Server || |
|
1100 paint.mType == eStyleSVGPaintType_ContextFill || |
|
1101 paint.mType == eStyleSVGPaintType_ContextStroke; |
|
1102 nscolor color = isServer ? paint.mFallbackColor : paint.mPaint.mColor; |
|
1103 if (styleIfVisited) { |
|
1104 const nsStyleSVGPaint &paintIfVisited = |
|
1105 styleIfVisited->StyleSVG()->*aFillOrStroke; |
|
1106 // To prevent Web content from detecting if a user has visited a URL |
|
1107 // (via URL loading triggered by paint servers or performance |
|
1108 // differences between paint servers or between a paint server and a |
|
1109 // color), we do not allow whether links are visited to change which |
|
1110 // paint server is used or switch between paint servers and simple |
|
1111 // colors. A :visited style may only override a simple color with |
|
1112 // another simple color. |
|
1113 if (paintIfVisited.mType == eStyleSVGPaintType_Color && |
|
1114 paint.mType == eStyleSVGPaintType_Color) { |
|
1115 nscolor colors[2] = { color, paintIfVisited.mPaint.mColor }; |
|
1116 return nsStyleContext::CombineVisitedColors( |
|
1117 colors, aStyleContext->RelevantLinkVisited()); |
|
1118 } |
|
1119 } |
|
1120 return color; |
|
1121 } |
|
1122 |
|
1123 static void |
|
1124 SetupFallbackOrPaintColor(gfxContext *aContext, nsStyleContext *aStyleContext, |
|
1125 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, |
|
1126 float aOpacity) |
|
1127 { |
|
1128 nscolor color = nsSVGUtils::GetFallbackOrPaintColor( |
|
1129 aContext, aStyleContext, aFillOrStroke); |
|
1130 |
|
1131 aContext->SetColor(gfxRGBA(NS_GET_R(color)/255.0, |
|
1132 NS_GET_G(color)/255.0, |
|
1133 NS_GET_B(color)/255.0, |
|
1134 NS_GET_A(color)/255.0 * aOpacity)); |
|
1135 } |
|
1136 |
|
1137 static float |
|
1138 MaybeOptimizeOpacity(nsIFrame *aFrame, float aFillOrStrokeOpacity) |
|
1139 { |
|
1140 float opacity = aFrame->StyleDisplay()->mOpacity; |
|
1141 if (opacity < 1 && nsSVGUtils::CanOptimizeOpacity(aFrame)) { |
|
1142 return aFillOrStrokeOpacity * opacity; |
|
1143 } |
|
1144 return aFillOrStrokeOpacity; |
|
1145 } |
|
1146 |
|
1147 /* static */ bool |
|
1148 nsSVGUtils::SetupContextPaint(gfxContext *aContext, |
|
1149 gfxTextContextPaint *aContextPaint, |
|
1150 const nsStyleSVGPaint &aPaint, |
|
1151 float aOpacity) |
|
1152 { |
|
1153 nsRefPtr<gfxPattern> pattern; |
|
1154 |
|
1155 if (!aContextPaint) { |
|
1156 return false; |
|
1157 } |
|
1158 |
|
1159 switch (aPaint.mType) { |
|
1160 case eStyleSVGPaintType_ContextFill: |
|
1161 pattern = aContextPaint->GetFillPattern(aOpacity, aContext->CurrentMatrix()); |
|
1162 break; |
|
1163 case eStyleSVGPaintType_ContextStroke: |
|
1164 pattern = aContextPaint->GetStrokePattern(aOpacity, aContext->CurrentMatrix()); |
|
1165 break; |
|
1166 default: |
|
1167 return false; |
|
1168 } |
|
1169 |
|
1170 if (!pattern) { |
|
1171 return false; |
|
1172 } |
|
1173 |
|
1174 aContext->SetPattern(pattern); |
|
1175 |
|
1176 return true; |
|
1177 } |
|
1178 |
|
1179 bool |
|
1180 nsSVGUtils::SetupCairoFillPaint(nsIFrame *aFrame, gfxContext* aContext, |
|
1181 gfxTextContextPaint *aContextPaint) |
|
1182 { |
|
1183 const nsStyleSVG* style = aFrame->StyleSVG(); |
|
1184 if (style->mFill.mType == eStyleSVGPaintType_None) |
|
1185 return false; |
|
1186 |
|
1187 if (style->mFillRule == NS_STYLE_FILL_RULE_EVENODD) |
|
1188 aContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD); |
|
1189 else |
|
1190 aContext->SetFillRule(gfxContext::FILL_RULE_WINDING); |
|
1191 |
|
1192 float opacity = MaybeOptimizeOpacity(aFrame, |
|
1193 GetOpacity(style->mFillOpacitySource, |
|
1194 style->mFillOpacity, |
|
1195 aContextPaint)); |
|
1196 nsSVGPaintServerFrame *ps = |
|
1197 nsSVGEffects::GetPaintServer(aFrame, &style->mFill, nsSVGEffects::FillProperty()); |
|
1198 if (ps && ps->SetupPaintServer(aContext, aFrame, &nsStyleSVG::mFill, opacity)) |
|
1199 return true; |
|
1200 |
|
1201 if (SetupContextPaint(aContext, aContextPaint, style->mFill, opacity)) { |
|
1202 return true; |
|
1203 } |
|
1204 |
|
1205 // On failure, use the fallback colour in case we have an |
|
1206 // objectBoundingBox where the width or height of the object is zero. |
|
1207 // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox |
|
1208 SetupFallbackOrPaintColor(aContext, aFrame->StyleContext(), |
|
1209 &nsStyleSVG::mFill, opacity); |
|
1210 |
|
1211 return true; |
|
1212 } |
|
1213 |
|
1214 bool |
|
1215 nsSVGUtils::SetupCairoStrokePaint(nsIFrame *aFrame, gfxContext* aContext, |
|
1216 gfxTextContextPaint *aContextPaint) |
|
1217 { |
|
1218 const nsStyleSVG* style = aFrame->StyleSVG(); |
|
1219 if (style->mStroke.mType == eStyleSVGPaintType_None) |
|
1220 return false; |
|
1221 |
|
1222 float opacity = MaybeOptimizeOpacity(aFrame, |
|
1223 GetOpacity(style->mStrokeOpacitySource, |
|
1224 style->mStrokeOpacity, |
|
1225 aContextPaint)); |
|
1226 |
|
1227 nsSVGPaintServerFrame *ps = |
|
1228 nsSVGEffects::GetPaintServer(aFrame, &style->mStroke, nsSVGEffects::StrokeProperty()); |
|
1229 if (ps && ps->SetupPaintServer(aContext, aFrame, &nsStyleSVG::mStroke, opacity)) |
|
1230 return true; |
|
1231 |
|
1232 if (SetupContextPaint(aContext, aContextPaint, style->mStroke, opacity)) { |
|
1233 return true; |
|
1234 } |
|
1235 |
|
1236 // On failure, use the fallback colour in case we have an |
|
1237 // objectBoundingBox where the width or height of the object is zero. |
|
1238 // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox |
|
1239 SetupFallbackOrPaintColor(aContext, aFrame->StyleContext(), |
|
1240 &nsStyleSVG::mStroke, opacity); |
|
1241 |
|
1242 return true; |
|
1243 } |
|
1244 |
|
1245 /* static */ float |
|
1246 nsSVGUtils::GetOpacity(nsStyleSVGOpacitySource aOpacityType, |
|
1247 const float& aOpacity, |
|
1248 gfxTextContextPaint *aOuterContextPaint) |
|
1249 { |
|
1250 float opacity = 1.0f; |
|
1251 switch (aOpacityType) { |
|
1252 case eStyleSVGOpacitySource_Normal: |
|
1253 opacity = aOpacity; |
|
1254 break; |
|
1255 case eStyleSVGOpacitySource_ContextFillOpacity: |
|
1256 if (aOuterContextPaint) { |
|
1257 opacity = aOuterContextPaint->GetFillOpacity(); |
|
1258 } else { |
|
1259 NS_WARNING("context-fill-opacity used outside of an SVG glyph"); |
|
1260 } |
|
1261 break; |
|
1262 case eStyleSVGOpacitySource_ContextStrokeOpacity: |
|
1263 if (aOuterContextPaint) { |
|
1264 opacity = aOuterContextPaint->GetStrokeOpacity(); |
|
1265 } else { |
|
1266 NS_WARNING("context-stroke-opacity used outside of an SVG glyph"); |
|
1267 } |
|
1268 break; |
|
1269 default: |
|
1270 NS_NOTREACHED("Unknown object opacity inheritance type for SVG glyph"); |
|
1271 } |
|
1272 return opacity; |
|
1273 } |
|
1274 |
|
1275 bool |
|
1276 nsSVGUtils::HasStroke(nsIFrame* aFrame, gfxTextContextPaint *aContextPaint) |
|
1277 { |
|
1278 const nsStyleSVG *style = aFrame->StyleSVG(); |
|
1279 return style->HasStroke() && GetStrokeWidth(aFrame, aContextPaint) > 0; |
|
1280 } |
|
1281 |
|
1282 float |
|
1283 nsSVGUtils::GetStrokeWidth(nsIFrame* aFrame, gfxTextContextPaint *aContextPaint) |
|
1284 { |
|
1285 const nsStyleSVG *style = aFrame->StyleSVG(); |
|
1286 if (aContextPaint && style->mStrokeWidthFromObject) { |
|
1287 return aContextPaint->GetStrokeWidth(); |
|
1288 } |
|
1289 |
|
1290 nsIContent* content = aFrame->GetContent(); |
|
1291 if (content->IsNodeOfType(nsINode::eTEXT)) { |
|
1292 content = content->GetParent(); |
|
1293 } |
|
1294 |
|
1295 nsSVGElement *ctx = static_cast<nsSVGElement*>(content); |
|
1296 |
|
1297 return SVGContentUtils::CoordToFloat(aFrame->PresContext(), ctx, |
|
1298 style->mStrokeWidth); |
|
1299 } |
|
1300 |
|
1301 void |
|
1302 nsSVGUtils::SetupCairoStrokeBBoxGeometry(nsIFrame* aFrame, |
|
1303 gfxContext *aContext, |
|
1304 gfxTextContextPaint *aContextPaint) |
|
1305 { |
|
1306 float width = GetStrokeWidth(aFrame, aContextPaint); |
|
1307 if (width <= 0) |
|
1308 return; |
|
1309 aContext->SetLineWidth(width); |
|
1310 |
|
1311 // Apply any stroke-specific transform |
|
1312 gfxMatrix strokeTransform = GetStrokeTransform(aFrame); |
|
1313 if (!strokeTransform.IsIdentity()) { |
|
1314 aContext->Multiply(strokeTransform); |
|
1315 } |
|
1316 |
|
1317 const nsStyleSVG* style = aFrame->StyleSVG(); |
|
1318 |
|
1319 switch (style->mStrokeLinecap) { |
|
1320 case NS_STYLE_STROKE_LINECAP_BUTT: |
|
1321 aContext->SetLineCap(gfxContext::LINE_CAP_BUTT); |
|
1322 break; |
|
1323 case NS_STYLE_STROKE_LINECAP_ROUND: |
|
1324 aContext->SetLineCap(gfxContext::LINE_CAP_ROUND); |
|
1325 break; |
|
1326 case NS_STYLE_STROKE_LINECAP_SQUARE: |
|
1327 aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE); |
|
1328 break; |
|
1329 } |
|
1330 |
|
1331 aContext->SetMiterLimit(style->mStrokeMiterlimit); |
|
1332 |
|
1333 switch (style->mStrokeLinejoin) { |
|
1334 case NS_STYLE_STROKE_LINEJOIN_MITER: |
|
1335 aContext->SetLineJoin(gfxContext::LINE_JOIN_MITER); |
|
1336 break; |
|
1337 case NS_STYLE_STROKE_LINEJOIN_ROUND: |
|
1338 aContext->SetLineJoin(gfxContext::LINE_JOIN_ROUND); |
|
1339 break; |
|
1340 case NS_STYLE_STROKE_LINEJOIN_BEVEL: |
|
1341 aContext->SetLineJoin(gfxContext::LINE_JOIN_BEVEL); |
|
1342 break; |
|
1343 } |
|
1344 } |
|
1345 |
|
1346 static bool |
|
1347 GetStrokeDashData(nsIFrame* aFrame, |
|
1348 FallibleTArray<gfxFloat>& aDashes, |
|
1349 gfxFloat* aDashOffset, |
|
1350 gfxTextContextPaint *aContextPaint) |
|
1351 { |
|
1352 const nsStyleSVG* style = aFrame->StyleSVG(); |
|
1353 nsPresContext *presContext = aFrame->PresContext(); |
|
1354 nsIContent *content = aFrame->GetContent(); |
|
1355 nsSVGElement *ctx = static_cast<nsSVGElement*> |
|
1356 (content->IsNodeOfType(nsINode::eTEXT) ? |
|
1357 content->GetParent() : content); |
|
1358 |
|
1359 gfxFloat totalLength = 0.0; |
|
1360 if (aContextPaint && style->mStrokeDasharrayFromObject) { |
|
1361 aDashes = aContextPaint->GetStrokeDashArray(); |
|
1362 |
|
1363 for (uint32_t i = 0; i < aDashes.Length(); i++) { |
|
1364 if (aDashes[i] < 0.0) { |
|
1365 return false; |
|
1366 } |
|
1367 totalLength += aDashes[i]; |
|
1368 } |
|
1369 |
|
1370 } else { |
|
1371 uint32_t count = style->mStrokeDasharrayLength; |
|
1372 if (!count || !aDashes.SetLength(count)) { |
|
1373 return false; |
|
1374 } |
|
1375 |
|
1376 gfxFloat pathScale = 1.0; |
|
1377 |
|
1378 if (content->Tag() == nsGkAtoms::path) { |
|
1379 pathScale = static_cast<SVGPathElement*>(content)-> |
|
1380 GetPathLengthScale(SVGPathElement::eForStroking); |
|
1381 if (pathScale <= 0) { |
|
1382 return false; |
|
1383 } |
|
1384 } |
|
1385 |
|
1386 const nsStyleCoord *dasharray = style->mStrokeDasharray; |
|
1387 |
|
1388 for (uint32_t i = 0; i < count; i++) { |
|
1389 aDashes[i] = SVGContentUtils::CoordToFloat(presContext, |
|
1390 ctx, |
|
1391 dasharray[i]) * pathScale; |
|
1392 if (aDashes[i] < 0.0) { |
|
1393 return false; |
|
1394 } |
|
1395 totalLength += aDashes[i]; |
|
1396 } |
|
1397 } |
|
1398 |
|
1399 if (aContextPaint && style->mStrokeDashoffsetFromObject) { |
|
1400 *aDashOffset = aContextPaint->GetStrokeDashOffset(); |
|
1401 } else { |
|
1402 *aDashOffset = SVGContentUtils::CoordToFloat(presContext, |
|
1403 ctx, |
|
1404 style->mStrokeDashoffset); |
|
1405 } |
|
1406 |
|
1407 return (totalLength > 0.0); |
|
1408 } |
|
1409 |
|
1410 void |
|
1411 nsSVGUtils::SetupCairoStrokeGeometry(nsIFrame* aFrame, gfxContext* aContext, |
|
1412 gfxTextContextPaint *aContextPaint) |
|
1413 { |
|
1414 SetupCairoStrokeBBoxGeometry(aFrame, aContext, aContextPaint); |
|
1415 |
|
1416 AutoFallibleTArray<gfxFloat, 10> dashes; |
|
1417 gfxFloat dashOffset; |
|
1418 if (GetStrokeDashData(aFrame, dashes, &dashOffset, aContextPaint)) { |
|
1419 aContext->SetDash(dashes.Elements(), dashes.Length(), dashOffset); |
|
1420 } |
|
1421 } |
|
1422 |
|
1423 uint16_t |
|
1424 nsSVGUtils::GetGeometryHitTestFlags(nsIFrame* aFrame) |
|
1425 { |
|
1426 uint16_t flags = 0; |
|
1427 |
|
1428 switch(aFrame->StyleVisibility()->mPointerEvents) { |
|
1429 case NS_STYLE_POINTER_EVENTS_NONE: |
|
1430 break; |
|
1431 case NS_STYLE_POINTER_EVENTS_AUTO: |
|
1432 case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED: |
|
1433 if (aFrame->StyleVisibility()->IsVisible()) { |
|
1434 if (aFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None) |
|
1435 flags |= SVG_HIT_TEST_FILL; |
|
1436 if (aFrame->StyleSVG()->mStroke.mType != eStyleSVGPaintType_None) |
|
1437 flags |= SVG_HIT_TEST_STROKE; |
|
1438 if (aFrame->StyleSVG()->mStrokeOpacity > 0) |
|
1439 flags |= SVG_HIT_TEST_CHECK_MRECT; |
|
1440 } |
|
1441 break; |
|
1442 case NS_STYLE_POINTER_EVENTS_VISIBLEFILL: |
|
1443 if (aFrame->StyleVisibility()->IsVisible()) { |
|
1444 flags |= SVG_HIT_TEST_FILL; |
|
1445 } |
|
1446 break; |
|
1447 case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE: |
|
1448 if (aFrame->StyleVisibility()->IsVisible()) { |
|
1449 flags |= SVG_HIT_TEST_STROKE; |
|
1450 } |
|
1451 break; |
|
1452 case NS_STYLE_POINTER_EVENTS_VISIBLE: |
|
1453 if (aFrame->StyleVisibility()->IsVisible()) { |
|
1454 flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE; |
|
1455 } |
|
1456 break; |
|
1457 case NS_STYLE_POINTER_EVENTS_PAINTED: |
|
1458 if (aFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None) |
|
1459 flags |= SVG_HIT_TEST_FILL; |
|
1460 if (aFrame->StyleSVG()->mStroke.mType != eStyleSVGPaintType_None) |
|
1461 flags |= SVG_HIT_TEST_STROKE; |
|
1462 if (aFrame->StyleSVG()->mStrokeOpacity) |
|
1463 flags |= SVG_HIT_TEST_CHECK_MRECT; |
|
1464 break; |
|
1465 case NS_STYLE_POINTER_EVENTS_FILL: |
|
1466 flags |= SVG_HIT_TEST_FILL; |
|
1467 break; |
|
1468 case NS_STYLE_POINTER_EVENTS_STROKE: |
|
1469 flags |= SVG_HIT_TEST_STROKE; |
|
1470 break; |
|
1471 case NS_STYLE_POINTER_EVENTS_ALL: |
|
1472 flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE; |
|
1473 break; |
|
1474 default: |
|
1475 NS_ERROR("not reached"); |
|
1476 break; |
|
1477 } |
|
1478 |
|
1479 return flags; |
|
1480 } |
|
1481 |
|
1482 bool |
|
1483 nsSVGUtils::SetupCairoStroke(nsIFrame* aFrame, gfxContext* aContext, |
|
1484 gfxTextContextPaint *aContextPaint) |
|
1485 { |
|
1486 if (!HasStroke(aFrame, aContextPaint)) { |
|
1487 return false; |
|
1488 } |
|
1489 SetupCairoStrokeGeometry(aFrame, aContext, aContextPaint); |
|
1490 |
|
1491 return SetupCairoStrokePaint(aFrame, aContext, aContextPaint); |
|
1492 } |
|
1493 |
|
1494 bool |
|
1495 nsSVGUtils::PaintSVGGlyph(Element* aElement, gfxContext* aContext, |
|
1496 DrawMode aDrawMode, |
|
1497 gfxTextContextPaint* aContextPaint) |
|
1498 { |
|
1499 nsIFrame* frame = aElement->GetPrimaryFrame(); |
|
1500 nsISVGChildFrame* svgFrame = do_QueryFrame(frame); |
|
1501 if (!svgFrame) { |
|
1502 return false; |
|
1503 } |
|
1504 nsRefPtr<nsRenderingContext> context(new nsRenderingContext()); |
|
1505 context->Init(frame->PresContext()->DeviceContext(), aContext); |
|
1506 context->AddUserData(&gfxTextContextPaint::sUserDataKey, aContextPaint, |
|
1507 nullptr); |
|
1508 svgFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED); |
|
1509 nsresult rv = svgFrame->PaintSVG(context, nullptr, frame); |
|
1510 return NS_SUCCEEDED(rv); |
|
1511 } |
|
1512 |
|
1513 bool |
|
1514 nsSVGUtils::GetSVGGlyphExtents(Element* aElement, |
|
1515 const gfxMatrix& aSVGToAppSpace, |
|
1516 gfxRect* aResult) |
|
1517 { |
|
1518 nsIFrame* frame = aElement->GetPrimaryFrame(); |
|
1519 nsISVGChildFrame* svgFrame = do_QueryFrame(frame); |
|
1520 if (!svgFrame) { |
|
1521 return false; |
|
1522 } |
|
1523 |
|
1524 gfxMatrix transform(aSVGToAppSpace); |
|
1525 nsIContent* content = frame->GetContent(); |
|
1526 if (content->IsSVG()) { |
|
1527 transform = static_cast<nsSVGElement*>(content)-> |
|
1528 PrependLocalTransformsTo(aSVGToAppSpace); |
|
1529 } |
|
1530 |
|
1531 *aResult = svgFrame->GetBBoxContribution(gfx::ToMatrix(transform), |
|
1532 nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIncludeFillGeometry | |
|
1533 nsSVGUtils::eBBoxIncludeStroke | nsSVGUtils::eBBoxIncludeStrokeGeometry | |
|
1534 nsSVGUtils::eBBoxIncludeMarkers).ToThebesRect(); |
|
1535 return true; |
|
1536 } |
|
1537 |
|
1538 nsRect |
|
1539 nsSVGUtils::ToCanvasBounds(const gfxRect &aUserspaceRect, |
|
1540 const gfxMatrix &aToCanvas, |
|
1541 const nsPresContext *presContext) |
|
1542 { |
|
1543 return nsLayoutUtils::RoundGfxRectToAppRect( |
|
1544 aToCanvas.TransformBounds(aUserspaceRect), |
|
1545 presContext->AppUnitsPerDevPixel()); |
|
1546 } |