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/. */
7 // This is also necessary to ensure our definition of M_SQRT1_2 is picked up
8 #include "nsSVGUtils.h"
11 // Keep others in (case-insensitive) order:
12 #include "gfx2DGlue.h"
13 #include "gfxContext.h"
14 #include "gfxMatrix.h"
15 #include "gfxPlatform.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"
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/SVGClipPathElement.h"
46 #include "mozilla/dom/SVGPathElement.h"
47 #include "nsSVGPathGeometryElement.h"
48 #include "nsSVGPathGeometryFrame.h"
49 #include "nsSVGPaintServerFrame.h"
50 #include "mozilla/dom/SVGSVGElement.h"
51 #include "nsTextFrame.h"
52 #include "SVGContentUtils.h"
53 #include "mozilla/unused.h"
55 using namespace mozilla
;
56 using namespace mozilla::dom
;
57 using namespace mozilla::gfx
;
59 static bool sSVGDisplayListHitTestingEnabled
;
60 static bool sSVGDisplayListPaintingEnabled
;
61 static bool sSVGNewGetBBoxEnabled
;
64 NS_SVGDisplayListHitTestingEnabled()
66 return sSVGDisplayListHitTestingEnabled
;
70 NS_SVGDisplayListPaintingEnabled()
72 return sSVGDisplayListPaintingEnabled
;
76 NS_SVGNewGetBBoxEnabled()
78 return sSVGNewGetBBoxEnabled
;
82 // we only take the address of this:
83 static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey
;
85 SVGAutoRenderState::SVGAutoRenderState(nsRenderingContext
*aContext
,
87 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL
)
89 , mOriginalRenderState(nullptr)
91 , mPaintingToWindow(false)
93 MOZ_GUARD_OBJECT_NOTIFIER_INIT
;
94 mOriginalRenderState
= aContext
->RemoveUserData(&sSVGAutoRenderStateKey
);
95 // We always remove ourselves from aContext before it dies, so
96 // passing nullptr as the destroy function is okay.
97 aContext
->AddUserData(&sSVGAutoRenderStateKey
, this, nullptr);
100 SVGAutoRenderState::~SVGAutoRenderState()
102 mContext
->RemoveUserData(&sSVGAutoRenderStateKey
);
103 if (mOriginalRenderState
) {
104 mContext
->AddUserData(&sSVGAutoRenderStateKey
, mOriginalRenderState
, nullptr);
109 SVGAutoRenderState::SetPaintingToWindow(bool aPaintingToWindow
)
111 mPaintingToWindow
= aPaintingToWindow
;
114 /* static */ SVGAutoRenderState::RenderMode
115 SVGAutoRenderState::GetRenderMode(nsRenderingContext
*aContext
)
117 void *state
= aContext
->GetUserData(&sSVGAutoRenderStateKey
);
119 return static_cast<SVGAutoRenderState
*>(state
)->mMode
;
125 SVGAutoRenderState::IsPaintingToWindow(nsRenderingContext
*aContext
)
127 void *state
= aContext
->GetUserData(&sSVGAutoRenderStateKey
);
129 return static_cast<SVGAutoRenderState
*>(state
)->mPaintingToWindow
;
137 Preferences::AddBoolVarCache(&sSVGDisplayListHitTestingEnabled
,
138 "svg.display-lists.hit-testing.enabled");
140 Preferences::AddBoolVarCache(&sSVGDisplayListPaintingEnabled
,
141 "svg.display-lists.painting.enabled");
143 Preferences::AddBoolVarCache(&sSVGNewGetBBoxEnabled
,
144 "svg.new-getBBox.enabled");
147 nsSVGDisplayContainerFrame
*
148 nsSVGUtils::GetNearestSVGViewport(nsIFrame
*aFrame
)
150 NS_ASSERTION(aFrame
->IsFrameOfType(nsIFrame::eSVG
), "SVG frame expected");
151 if (aFrame
->GetType() == nsGkAtoms::svgOuterSVGFrame
) {
154 while ((aFrame
= aFrame
->GetParent())) {
155 NS_ASSERTION(aFrame
->IsFrameOfType(nsIFrame::eSVG
), "SVG frame expected");
156 if (aFrame
->GetType() == nsGkAtoms::svgInnerSVGFrame
||
157 aFrame
->GetType() == nsGkAtoms::svgOuterSVGFrame
) {
158 return do_QueryFrame(aFrame
);
161 NS_NOTREACHED("This is not reached. It's only needed to compile.");
166 nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame
*aFrame
,
167 const nsRect
&aPreFilterRect
)
169 NS_ABORT_IF_FALSE(aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
,
170 "Called on invalid frame type");
172 nsSVGFilterProperty
*property
= nsSVGEffects::GetFilterProperty(aFrame
);
173 if (!property
|| !property
->ReferencesValidResources()) {
174 return aPreFilterRect
;
177 return nsFilterInstance::GetPostFilterBounds(aFrame
, nullptr, &aPreFilterRect
);
181 nsSVGUtils::OuterSVGIsCallingReflowSVG(nsIFrame
*aFrame
)
183 return GetOuterSVGFrame(aFrame
)->IsCallingReflowSVG();
187 nsSVGUtils::AnyOuterSVGIsCallingReflowSVG(nsIFrame
* aFrame
)
189 nsSVGOuterSVGFrame
* outer
= GetOuterSVGFrame(aFrame
);
191 if (outer
->IsCallingReflowSVG()) {
194 outer
= GetOuterSVGFrame(outer
->GetParent());
200 nsSVGUtils::ScheduleReflowSVG(nsIFrame
*aFrame
)
202 NS_ABORT_IF_FALSE(aFrame
->IsFrameOfType(nsIFrame::eSVG
),
203 "Passed bad frame!");
205 // If this is triggered, the callers should be fixed to call us before
206 // ReflowSVG is called. If we try to mark dirty bits on frames while we're
207 // in the process of removing them, things will get messed up.
208 NS_ASSERTION(!OuterSVGIsCallingReflowSVG(aFrame
),
209 "Do not call under nsISVGChildFrame::ReflowSVG!");
211 // We don't call nsSVGEffects::InvalidateRenderingObservers here because
212 // we should only be called under InvalidateAndScheduleReflowSVG (which
213 // calls InvalidateBounds) or nsSVGDisplayContainerFrame::InsertFrames
214 // (at which point the frame has no observers).
216 if (aFrame
->GetStateBits() & NS_FRAME_IS_NONDISPLAY
) {
220 if (aFrame
->GetStateBits() &
221 (NS_FRAME_IS_DIRTY
| NS_FRAME_FIRST_REFLOW
)) {
222 // Nothing to do if we're already dirty, or if the outer-<svg>
223 // hasn't yet had its initial reflow.
227 nsSVGOuterSVGFrame
*outerSVGFrame
= nullptr;
229 // We must not add dirty bits to the nsSVGOuterSVGFrame or else
230 // PresShell::FrameNeedsReflow won't work when we pass it in below.
231 if (aFrame
->GetStateBits() & NS_STATE_IS_OUTER_SVG
) {
232 outerSVGFrame
= static_cast<nsSVGOuterSVGFrame
*>(aFrame
);
234 aFrame
->AddStateBits(NS_FRAME_IS_DIRTY
);
236 nsIFrame
*f
= aFrame
->GetParent();
237 while (f
&& !(f
->GetStateBits() & NS_STATE_IS_OUTER_SVG
)) {
238 if (f
->GetStateBits() &
239 (NS_FRAME_IS_DIRTY
| NS_FRAME_HAS_DIRTY_CHILDREN
)) {
242 f
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
244 NS_ABORT_IF_FALSE(f
->IsFrameOfType(nsIFrame::eSVG
),
245 "NS_STATE_IS_OUTER_SVG check above not valid!");
248 outerSVGFrame
= static_cast<nsSVGOuterSVGFrame
*>(f
);
250 NS_ABORT_IF_FALSE(outerSVGFrame
&&
251 outerSVGFrame
->GetType() == nsGkAtoms::svgOuterSVGFrame
,
252 "Did not find nsSVGOuterSVGFrame!");
255 if (outerSVGFrame
->GetStateBits() & NS_FRAME_IN_REFLOW
) {
256 // We're currently under an nsSVGOuterSVGFrame::Reflow call so there is no
257 // need to call PresShell::FrameNeedsReflow, since we have an
258 // nsSVGOuterSVGFrame::DidReflow call pending.
262 nsFrameState dirtyBit
=
263 (outerSVGFrame
== aFrame
? NS_FRAME_IS_DIRTY
: NS_FRAME_HAS_DIRTY_CHILDREN
);
265 aFrame
->PresContext()->PresShell()->FrameNeedsReflow(
266 outerSVGFrame
, nsIPresShell::eResize
, dirtyBit
);
270 nsSVGUtils::NeedsReflowSVG(nsIFrame
*aFrame
)
272 NS_ABORT_IF_FALSE(aFrame
->IsFrameOfType(nsIFrame::eSVG
),
273 "SVG uses bits differently!");
275 // The flags we test here may change, hence why we have this separate
277 return NS_SUBTREE_DIRTY(aFrame
);
281 nsSVGUtils::NotifyAncestorsOfFilterRegionChange(nsIFrame
*aFrame
)
283 NS_ABORT_IF_FALSE(!(aFrame
->GetStateBits() & NS_STATE_IS_OUTER_SVG
),
284 "Not expecting to be called on the outer SVG Frame");
286 aFrame
= aFrame
->GetParent();
289 if (aFrame
->GetStateBits() & NS_STATE_IS_OUTER_SVG
)
292 nsSVGFilterProperty
*property
= nsSVGEffects::GetFilterProperty(aFrame
);
294 property
->Invalidate();
296 aFrame
= aFrame
->GetParent();
301 nsSVGUtils::ObjectSpace(const gfxRect
&aRect
, const nsSVGLength2
*aLength
)
305 switch (aLength
->GetCtxType()) {
306 case SVGContentUtils::X
:
307 axis
= aRect
.Width();
309 case SVGContentUtils::Y
:
310 axis
= aRect
.Height();
312 case SVGContentUtils::XY
:
313 axis
= float(SVGContentUtils::ComputeNormalizedHypotenuse(
314 aRect
.Width(), aRect
.Height()));
317 NS_NOTREACHED("unexpected ctx type");
321 if (aLength
->IsPercentage()) {
322 // Multiply first to avoid precision errors:
323 return axis
* aLength
->GetAnimValInSpecifiedUnits() / 100;
325 return aLength
->GetAnimValue(static_cast<SVGSVGElement
*>(nullptr)) * axis
;
329 nsSVGUtils::UserSpace(nsSVGElement
*aSVGElement
, const nsSVGLength2
*aLength
)
331 return aLength
->GetAnimValue(aSVGElement
);
335 nsSVGUtils::UserSpace(nsIFrame
*aNonSVGContext
, const nsSVGLength2
*aLength
)
337 return aLength
->GetAnimValue(aNonSVGContext
);
341 nsSVGUtils::GetOuterSVGFrame(nsIFrame
*aFrame
)
344 if (aFrame
->GetStateBits() & NS_STATE_IS_OUTER_SVG
) {
345 return static_cast<nsSVGOuterSVGFrame
*>(aFrame
);
347 aFrame
= aFrame
->GetParent();
354 nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame
* aFrame
, nsRect
* aRect
)
356 nsISVGChildFrame
* svg
= do_QueryFrame(aFrame
);
359 nsSVGOuterSVGFrame
* outer
= GetOuterSVGFrame(aFrame
);
363 *aRect
= (aFrame
->GetStateBits() & NS_FRAME_IS_NONDISPLAY
) ?
364 nsRect(0, 0, 0, 0) : svg
->GetCoveredRegion();
369 nsSVGUtils::GetCanvasTM(nsIFrame
*aFrame
, uint32_t aFor
,
370 nsIFrame
* aTransformRoot
)
372 // XXX yuck, we really need a common interface for GetCanvasTM
374 if (!aFrame
->IsFrameOfType(nsIFrame::eSVG
)) {
375 if (aFor
== nsISVGChildFrame::FOR_HIT_TESTING
&&
376 NS_SVGDisplayListHitTestingEnabled()) {
379 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame
);
382 if (!(aFrame
->GetStateBits() & NS_FRAME_IS_NONDISPLAY
) &&
384 if (aFor
== nsISVGChildFrame::FOR_PAINTING
&&
385 NS_SVGDisplayListPaintingEnabled()) {
386 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame
);
388 if (aFor
== nsISVGChildFrame::FOR_HIT_TESTING
&&
389 NS_SVGDisplayListHitTestingEnabled()) {
394 nsIAtom
* type
= aFrame
->GetType();
395 if (type
== nsGkAtoms::svgForeignObjectFrame
) {
396 return static_cast<nsSVGForeignObjectFrame
*>(aFrame
)->
397 GetCanvasTM(aFor
, aTransformRoot
);
399 if (type
== nsGkAtoms::svgOuterSVGFrame
) {
400 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame
);
403 nsSVGContainerFrame
*containerFrame
= do_QueryFrame(aFrame
);
404 if (containerFrame
) {
405 return containerFrame
->GetCanvasTM(aFor
, aTransformRoot
);
408 return static_cast<nsSVGPathGeometryFrame
*>(aFrame
)->
409 GetCanvasTM(aFor
, aTransformRoot
);
413 nsSVGUtils::GetUserToCanvasTM(nsIFrame
*aFrame
, uint32_t aFor
)
415 NS_ASSERTION(aFor
== nsISVGChildFrame::FOR_OUTERSVG_TM
,
418 nsISVGChildFrame
* svgFrame
= do_QueryFrame(aFrame
);
419 NS_ASSERTION(svgFrame
, "bad frame");
423 nsSVGElement
*content
= static_cast<nsSVGElement
*>(aFrame
->GetContent());
424 tm
= content
->PrependLocalTransformsTo(
425 GetCanvasTM(aFrame
->GetParent(), aFor
),
426 nsSVGElement::eUserSpaceToParent
);
432 nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame
*aFrame
, uint32_t aFlags
)
434 nsIFrame
*kid
= aFrame
->GetFirstPrincipalChild();
437 nsISVGChildFrame
* SVGFrame
= do_QueryFrame(kid
);
439 SVGFrame
->NotifySVGChanged(aFlags
);
441 NS_ASSERTION(kid
->IsFrameOfType(nsIFrame::eSVG
) || kid
->IsSVGText(),
442 "SVG frame expected");
443 // recurse into the children of container frames e.g. <clipPath>, <mask>
444 // in case they have child frames with transformation matrices
445 if (kid
->IsFrameOfType(nsIFrame::eSVG
)) {
446 NotifyChildrenOfSVGChange(kid
, aFlags
);
449 kid
= kid
->GetNextSibling();
453 // ************************************************************
455 class SVGPaintCallback
: public nsSVGFilterPaintCallback
458 virtual void Paint(nsRenderingContext
*aContext
, nsIFrame
*aTarget
,
459 const nsIntRect
* aDirtyRect
,
460 nsIFrame
* aTransformRoot
) MOZ_OVERRIDE
462 nsISVGChildFrame
*svgChildFrame
= do_QueryFrame(aTarget
);
463 NS_ASSERTION(svgChildFrame
, "Expected SVG frame here");
465 nsIntRect
* dirtyRect
= nullptr;
466 nsIntRect tmpDirtyRect
;
468 // aDirtyRect is in user-space pixels, we need to convert to
469 // outer-SVG-frame-relative device pixels.
471 gfxMatrix userToDeviceSpace
=
472 nsSVGUtils::GetCanvasTM(aTarget
, nsISVGChildFrame::FOR_PAINTING
, aTransformRoot
);
473 if (userToDeviceSpace
.IsSingular()) {
476 gfxRect dirtyBounds
= userToDeviceSpace
.TransformBounds(
477 gfxRect(aDirtyRect
->x
, aDirtyRect
->y
, aDirtyRect
->width
, aDirtyRect
->height
));
478 dirtyBounds
.RoundOut();
479 if (gfxUtils::GfxRectToIntRect(dirtyBounds
, &tmpDirtyRect
)) {
480 dirtyRect
= &tmpDirtyRect
;
484 svgChildFrame
->PaintSVG(aContext
, dirtyRect
, aTransformRoot
);
489 nsSVGUtils::PaintFrameWithEffects(nsRenderingContext
*aContext
,
490 const nsIntRect
*aDirtyRect
,
492 nsIFrame
*aTransformRoot
)
494 NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
495 (aFrame
->GetStateBits() & NS_FRAME_IS_NONDISPLAY
) ||
496 aFrame
->PresContext()->IsGlyph(),
497 "If display lists are enabled, only painting of non-display "
498 "SVG should take this code path");
500 nsISVGChildFrame
*svgChildFrame
= do_QueryFrame(aFrame
);
504 float opacity
= aFrame
->StyleDisplay()->mOpacity
;
508 const nsIContent
* content
= aFrame
->GetContent();
509 if (content
->IsSVG() &&
510 !static_cast<const nsSVGElement
*>(content
)->HasValidDimensions()) {
514 /* Properties are added lazily and may have been removed by a restyle,
515 so make sure all applicable ones are set again. */
517 nsSVGEffects::EffectProperties effectProperties
=
518 nsSVGEffects::GetEffectProperties(aFrame
);
520 bool isOK
= effectProperties
.HasNoFilterOrHasValidFilter();
523 !(aFrame
->GetStateBits() & NS_FRAME_IS_NONDISPLAY
)) {
524 // Here we convert aFrame's paint bounds to outer-<svg> device space,
525 // compare it to aDirtyRect, and return early if they don't intersect.
526 // We don't do this optimization for nondisplay SVG since nondisplay
527 // SVG doesn't maintain bounds/overflow rects.
528 nsRect overflowRect
= aFrame
->GetVisualOverflowRectRelativeToSelf();
529 if (aFrame
->IsFrameOfType(nsIFrame::eSVGGeometry
) ||
530 aFrame
->IsSVGText()) {
531 // Unlike containers, leaf frames do not include GetPosition() in
533 overflowRect
= overflowRect
+ aFrame
->GetPosition();
535 int32_t appUnitsPerDevPx
= aFrame
->PresContext()->AppUnitsPerDevPixel();
536 gfxMatrix tm
= GetCanvasTM(aFrame
, nsISVGChildFrame::FOR_PAINTING
, aTransformRoot
);
537 if (aFrame
->IsFrameOfType(nsIFrame::eSVG
| nsIFrame::eSVGContainer
)) {
538 gfx::Matrix childrenOnlyTM
;
539 if (static_cast<nsSVGContainerFrame
*>(aFrame
)->
540 HasChildrenOnlyTransform(&childrenOnlyTM
)) {
541 // Undo the children-only transform:
542 if (!childrenOnlyTM
.Invert()) {
545 tm
= ThebesMatrix(childrenOnlyTM
) * tm
;
548 nsIntRect bounds
= TransformFrameRectToOuterSVG(overflowRect
,
549 tm
, aFrame
->PresContext()).
550 ToOutsidePixels(appUnitsPerDevPx
);
551 if (!aDirtyRect
->Intersects(bounds
)) {
556 /* SVG defines the following rendering model:
562 * 5. Apply clipping, masking, group opacity
564 * We follow this, but perform a couple of optimizations:
566 * + Use cairo's clipPath when representable natively (single object
569 * + Merge opacity and masking if both used together.
572 if (opacity
!= 1.0f
&& CanOptimizeOpacity(aFrame
))
575 gfxContext
*gfx
= aContext
->ThebesContext();
576 bool complexEffects
= false;
578 nsSVGClipPathFrame
*clipPathFrame
= effectProperties
.GetClipPathFrame(&isOK
);
579 nsSVGMaskFrame
*maskFrame
= effectProperties
.GetMaskFrame(&isOK
);
581 bool isTrivialClip
= clipPathFrame
? clipPathFrame
->IsTrivial() : true;
584 // Some resource is invalid. We shouldn't paint anything.
589 if (clipPathFrame
|| maskFrame
)
590 matrix
= GetCanvasTM(aFrame
, nsISVGChildFrame::FOR_PAINTING
, aTransformRoot
);
592 /* Check if we need to do additional operations on this child's
593 * rendering, which necessitates rendering into another surface. */
594 if (opacity
!= 1.0f
|| maskFrame
|| (clipPathFrame
&& !isTrivialClip
)
595 || aFrame
->StyleDisplay()->mMixBlendMode
!= NS_STYLE_BLEND_NORMAL
) {
596 complexEffects
= true;
598 if (!(aFrame
->GetStateBits() & NS_FRAME_IS_NONDISPLAY
)) {
599 // aFrame has a valid visual overflow rect, so clip to it before calling
600 // PushGroup() to minimize the size of the surfaces we'll composite:
601 gfxContextMatrixAutoSaveRestore
matrixAutoSaveRestore(gfx
);
602 gfx
->Multiply(GetCanvasTM(aFrame
, nsISVGChildFrame::FOR_PAINTING
, aTransformRoot
));
603 nsRect overflowRect
= aFrame
->GetVisualOverflowRectRelativeToSelf();
604 if (aFrame
->IsFrameOfType(nsIFrame::eSVGGeometry
) ||
605 aFrame
->IsSVGText()) {
606 // Unlike containers, leaf frames do not include GetPosition() in
608 overflowRect
= overflowRect
+ aFrame
->GetPosition();
610 aContext
->IntersectClip(overflowRect
);
612 gfx
->PushGroup(gfxContentType::COLOR_ALPHA
);
615 /* If this frame has only a trivial clipPath, set up cairo's clipping now so
616 * we can just do normal painting and get it clipped appropriately.
618 if (clipPathFrame
&& isTrivialClip
) {
620 clipPathFrame
->ClipPaint(aContext
, aFrame
, matrix
);
623 /* Paint the child */
624 if (effectProperties
.HasValidFilter()) {
625 nsRegion
* dirtyRegion
= nullptr;
626 nsRegion tmpDirtyRegion
;
628 // aDirtyRect is in outer-<svg> device pixels, but the filter code needs
629 // it in frame space.
630 gfxMatrix userToDeviceSpace
=
631 GetUserToCanvasTM(aFrame
, nsISVGChildFrame::FOR_OUTERSVG_TM
);
632 if (userToDeviceSpace
.IsSingular()) {
635 gfxMatrix deviceToUserSpace
= userToDeviceSpace
;
636 deviceToUserSpace
.Invert();
637 gfxRect dirtyBounds
= deviceToUserSpace
.TransformBounds(
638 gfxRect(aDirtyRect
->x
, aDirtyRect
->y
,
639 aDirtyRect
->width
, aDirtyRect
->height
));
641 nsLayoutUtils::RoundGfxRectToAppRect(
642 dirtyBounds
, aFrame
->PresContext()->AppUnitsPerCSSPixel()) -
643 aFrame
->GetPosition();
644 dirtyRegion
= &tmpDirtyRegion
;
646 SVGPaintCallback paintCallback
;
647 nsFilterInstance::PaintFilteredFrame(aContext
, aFrame
, &paintCallback
,
648 dirtyRegion
, aTransformRoot
);
650 svgChildFrame
->PaintSVG(aContext
, aDirtyRect
, aTransformRoot
);
653 if (clipPathFrame
&& isTrivialClip
) {
657 /* No more effects, we're done. */
661 gfx
->PopGroupToSource();
663 nsRefPtr
<gfxPattern
> maskSurface
=
664 maskFrame
? maskFrame
->GetMaskForMaskedFrame(aContext
->ThebesContext(),
665 aFrame
, matrix
, opacity
)
668 nsRefPtr
<gfxPattern
> clipMaskSurface
;
669 if (clipPathFrame
&& !isTrivialClip
) {
670 gfx
->PushGroup(gfxContentType::COLOR_ALPHA
);
672 nsresult rv
= clipPathFrame
->ClipPaint(aContext
, aFrame
, matrix
);
673 clipMaskSurface
= gfx
->PopGroup();
675 if (NS_SUCCEEDED(rv
) && clipMaskSurface
) {
676 // Still more set after clipping, so clip to another surface
677 if (maskSurface
|| opacity
!= 1.0f
) {
678 gfx
->PushGroup(gfxContentType::COLOR_ALPHA
);
679 gfx
->Mask(clipMaskSurface
);
680 gfx
->PopGroupToSource();
682 gfx
->Mask(clipMaskSurface
);
688 gfx
->Mask(maskSurface
);
689 } else if (opacity
!= 1.0f
) {
697 nsSVGUtils::HitTestClip(nsIFrame
*aFrame
, const nsPoint
&aPoint
)
699 nsSVGEffects::EffectProperties props
=
700 nsSVGEffects::GetEffectProperties(aFrame
);
701 if (!props
.mClipPath
)
705 nsSVGClipPathFrame
*clipPathFrame
= props
.GetClipPathFrame(&isOK
);
707 // clipPath is not a valid resource, so nothing gets painted, so
708 // hit-testing must fail.
711 if (!clipPathFrame
) {
712 // clipPath doesn't exist, ignore it.
716 return clipPathFrame
->ClipHitTest(aFrame
, GetCanvasTM(aFrame
,
717 nsISVGChildFrame::FOR_HIT_TESTING
), aPoint
);
721 nsSVGUtils::HitTestChildren(nsIFrame
*aFrame
, const nsPoint
&aPoint
)
723 // Traverse the list in reverse order, so that if we get a hit we know that's
724 // the topmost frame that intersects the point; then we can just return it.
725 nsIFrame
* result
= nullptr;
726 for (nsIFrame
* current
= aFrame
->PrincipalChildList().LastChild();
728 current
= current
->GetPrevSibling()) {
729 nsISVGChildFrame
* SVGFrame
= do_QueryFrame(current
);
731 const nsIContent
* content
= current
->GetContent();
732 if (content
->IsSVG() &&
733 !static_cast<const nsSVGElement
*>(content
)->HasValidDimensions()) {
736 result
= SVGFrame
->GetFrameForPoint(aPoint
);
742 if (result
&& !HitTestClip(aFrame
, aPoint
))
749 nsSVGUtils::GetCoveredRegion(const nsFrameList
&aFrames
)
753 for (nsIFrame
* kid
= aFrames
.FirstChild();
755 kid
= kid
->GetNextSibling()) {
756 nsISVGChildFrame
* child
= do_QueryFrame(kid
);
758 nsRect childRect
= child
->GetCoveredRegion();
759 rect
.UnionRect(rect
, childRect
);
767 nsSVGUtils::TransformOuterSVGPointToChildFrame(nsPoint aPoint
,
768 const gfxMatrix
& aFrameToCanvasTM
,
769 nsPresContext
* aPresContext
)
771 NS_ABORT_IF_FALSE(!aFrameToCanvasTM
.IsSingular(),
772 "Callers must not pass a singular matrix");
773 gfxMatrix canvasDevToFrameUserSpace
= aFrameToCanvasTM
;
774 canvasDevToFrameUserSpace
.Invert();
776 gfxPoint(aPoint
.x
, aPoint
.y
) / aPresContext
->AppUnitsPerCSSPixel();
777 gfxPoint userPt
= canvasDevToFrameUserSpace
.Transform(cssPxPt
);
778 gfxPoint appPt
= (userPt
* aPresContext
->AppUnitsPerCSSPixel()).Round();
779 userPt
.x
= clamped(appPt
.x
, gfxFloat(nscoord_MIN
), gfxFloat(nscoord_MAX
));
780 userPt
.y
= clamped(appPt
.y
, gfxFloat(nscoord_MIN
), gfxFloat(nscoord_MAX
));
781 // now guaranteed to be safe:
782 return nsPoint(nscoord(userPt
.x
), nscoord(userPt
.y
));
786 nsSVGUtils::TransformFrameRectToOuterSVG(const nsRect
& aRect
,
787 const gfxMatrix
& aMatrix
,
788 nsPresContext
* aPresContext
)
790 gfxRect
r(aRect
.x
, aRect
.y
, aRect
.width
, aRect
.height
);
791 r
.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
792 return nsLayoutUtils::RoundGfxRectToAppRect(
793 aMatrix
.TransformBounds(r
), aPresContext
->AppUnitsPerDevPixel());
797 nsSVGUtils::ConvertToSurfaceSize(const gfxSize
& aSize
,
798 bool *aResultOverflows
)
800 gfxIntSize
surfaceSize(ClampToInt(ceil(aSize
.width
)), ClampToInt(ceil(aSize
.height
)));
802 *aResultOverflows
= surfaceSize
.width
!= ceil(aSize
.width
) ||
803 surfaceSize
.height
!= ceil(aSize
.height
);
805 if (!gfxASurface::CheckSurfaceSize(surfaceSize
)) {
806 surfaceSize
.width
= std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION
,
808 surfaceSize
.height
= std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION
,
810 *aResultOverflows
= true;
817 nsSVGUtils::HitTestRect(const gfx::Matrix
&aMatrix
,
818 float aRX
, float aRY
, float aRWidth
, float aRHeight
,
821 gfx::Rect
rect(aRX
, aRY
, aRWidth
, aRHeight
);
822 if (rect
.IsEmpty() || aMatrix
.IsSingular()) {
825 gfx::Matrix toRectSpace
= aMatrix
;
826 toRectSpace
.Invert();
827 gfx::Point p
= toRectSpace
* gfx::Point(aX
, aY
);
828 return rect
.x
<= p
.x
&& p
.x
<= rect
.XMost() &&
829 rect
.y
<= p
.y
&& p
.y
<= rect
.YMost();
833 nsSVGUtils::GetClipRectForFrame(nsIFrame
*aFrame
,
834 float aX
, float aY
, float aWidth
, float aHeight
)
836 const nsStyleDisplay
* disp
= aFrame
->StyleDisplay();
838 if (!(disp
->mClipFlags
& NS_STYLE_CLIP_RECT
)) {
839 NS_ASSERTION(disp
->mClipFlags
== NS_STYLE_CLIP_AUTO
,
840 "We don't know about this type of clip.");
841 return gfxRect(aX
, aY
, aWidth
, aHeight
);
844 if (disp
->mOverflowX
== NS_STYLE_OVERFLOW_HIDDEN
||
845 disp
->mOverflowY
== NS_STYLE_OVERFLOW_HIDDEN
) {
847 nsIntRect clipPxRect
=
848 disp
->mClip
.ToOutsidePixels(aFrame
->PresContext()->AppUnitsPerDevPixel());
850 gfxRect(clipPxRect
.x
, clipPxRect
.y
, clipPxRect
.width
, clipPxRect
.height
);
852 if (NS_STYLE_CLIP_RIGHT_AUTO
& disp
->mClipFlags
) {
853 clipRect
.width
= aWidth
- clipRect
.X();
855 if (NS_STYLE_CLIP_BOTTOM_AUTO
& disp
->mClipFlags
) {
856 clipRect
.height
= aHeight
- clipRect
.Y();
859 if (disp
->mOverflowX
!= NS_STYLE_OVERFLOW_HIDDEN
) {
861 clipRect
.width
= aWidth
;
863 if (disp
->mOverflowY
!= NS_STYLE_OVERFLOW_HIDDEN
) {
865 clipRect
.height
= aHeight
;
870 return gfxRect(aX
, aY
, aWidth
, aHeight
);
874 nsSVGUtils::SetClipRect(gfxContext
*aContext
,
875 const gfxMatrix
&aCTM
,
876 const gfxRect
&aRect
)
878 if (aCTM
.IsSingular())
881 gfxContextMatrixAutoSaveRestore
matrixAutoSaveRestore(aContext
);
882 aContext
->Multiply(aCTM
);
883 aContext
->Clip(aRect
);
887 nsSVGUtils::GetBBox(nsIFrame
*aFrame
, uint32_t aFlags
)
889 if (aFrame
->GetContent()->IsNodeOfType(nsINode::eTEXT
)) {
890 aFrame
= aFrame
->GetParent();
893 nsISVGChildFrame
*svg
= do_QueryFrame(aFrame
);
894 if (svg
|| aFrame
->IsSVGText()) {
895 // It is possible to apply a gradient, pattern, clipping path, mask or
896 // filter to text. When one of these facilities is applied to text
897 // the bounding box is the entire text element in all
899 if (aFrame
->IsSVGText()) {
900 nsIFrame
* ancestor
= GetFirstNonAAncestorFrame(aFrame
);
901 if (ancestor
&& ancestor
->IsSVGText()) {
902 while (ancestor
->GetType() != nsGkAtoms::svgTextFrame
) {
903 ancestor
= ancestor
->GetParent();
906 svg
= do_QueryFrame(ancestor
);
908 nsIContent
* content
= aFrame
->GetContent();
909 if (content
->IsSVG() &&
910 !static_cast<const nsSVGElement
*>(content
)->HasValidDimensions()) {
914 if (aFrame
->GetType() == nsGkAtoms::svgForeignObjectFrame
||
915 aFrame
->GetType() == nsGkAtoms::svgUseFrame
) {
916 // The spec says getBBox "Returns the tight bounding box in *current user
917 // space*". So we should really be doing this for all elements, but that
918 // needs investigation to check that we won't break too much content.
919 // NOTE: When changing this to apply to other frame types, make sure to
920 // also update nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset.
921 NS_ABORT_IF_FALSE(content
->IsSVG(), "bad cast");
922 nsSVGElement
*element
= static_cast<nsSVGElement
*>(content
);
923 matrix
= element
->PrependLocalTransformsTo(matrix
,
924 nsSVGElement::eChildToUserSpace
);
926 bbox
= svg
->GetBBoxContribution(ToMatrix(matrix
), aFlags
).ToThebesRect();
927 // Account for 'clipped'.
928 if (aFlags
& nsSVGUtils::eBBoxIncludeClipped
) {
929 gfxRect
clipRect(0, 0, 0, 0);
930 float x
, y
, width
, height
;
933 svg
->GetBBoxContribution(ToMatrix(tm
),
934 nsSVGUtils::eBBoxIncludeFill
).ToThebesRect();
937 width
= fillBBox
.width
;
938 height
= fillBBox
.height
;
939 bool hasClip
= aFrame
->StyleDisplay()->IsScrollableOverflow();
942 nsSVGUtils::GetClipRectForFrame(aFrame
, x
, y
, width
, height
);
943 if (aFrame
->GetType() == nsGkAtoms::svgForeignObjectFrame
||
944 aFrame
->GetType() == nsGkAtoms::svgUseFrame
) {
945 clipRect
= matrix
.TransformBounds(clipRect
);
948 nsSVGEffects::EffectProperties effectProperties
=
949 nsSVGEffects::GetEffectProperties(aFrame
);
951 nsSVGClipPathFrame
*clipPathFrame
=
952 effectProperties
.GetClipPathFrame(&isOK
);
953 if (clipPathFrame
&& isOK
) {
954 SVGClipPathElement
*clipContent
=
955 static_cast<SVGClipPathElement
*>(clipPathFrame
->GetContent());
956 nsRefPtr
<SVGAnimatedEnumeration
> units
= clipContent
->ClipPathUnits();
957 if (units
->AnimVal() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
) {
958 matrix
= gfxMatrix().Scale(width
, height
) *
959 gfxMatrix().Translate(gfxPoint(x
, y
)) *
961 } else if (aFrame
->GetType() == nsGkAtoms::svgForeignObjectFrame
) {
965 clipPathFrame
->GetBBoxForClipPathFrame(bbox
, matrix
).ToThebesRect();
967 bbox
= bbox
.Intersect(clipRect
);
971 bbox
= gfxRect(0, 0, 0, 0);
974 bbox
= bbox
.Intersect(clipRect
);
978 if (bbox
.IsEmpty()) {
979 bbox
= gfxRect(0, 0, 0, 0);
984 return nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame
);
988 nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame
*aFrame
)
990 if (!(aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
)) {
991 // The user space for non-SVG frames is defined as the bounding box of the
992 // frame's border-box rects over all continuations.
996 // Leaf frames apply their own offset inside their user space.
997 if (aFrame
->IsFrameOfType(nsIFrame::eSVGGeometry
) ||
998 aFrame
->IsSVGText()) {
999 return nsLayoutUtils::RectToGfxRect(aFrame
->GetRect(),
1000 nsPresContext::AppUnitsPerCSSPixel()).TopLeft();
1003 // For foreignObject frames, nsSVGUtils::GetBBox applies their local
1004 // transform, so we need to do the same here.
1005 if (aFrame
->GetType() == nsGkAtoms::svgForeignObjectFrame
||
1006 aFrame
->GetType() == nsGkAtoms::svgUseFrame
) {
1007 gfxMatrix transform
= static_cast<nsSVGElement
*>(aFrame
->GetContent())->
1008 PrependLocalTransformsTo(gfxMatrix(),
1009 nsSVGElement::eChildToUserSpace
);
1010 NS_ASSERTION(!transform
.HasNonTranslation(), "we're relying on this being an offset-only transform");
1011 return transform
.GetTranslation();
1018 nsSVGUtils::GetRelativeRect(uint16_t aUnits
, const nsSVGLength2
*aXYWH
,
1019 const gfxRect
&aBBox
, nsIFrame
*aFrame
)
1021 float x
, y
, width
, height
;
1022 if (aUnits
== SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
) {
1023 x
= aBBox
.X() + ObjectSpace(aBBox
, &aXYWH
[0]);
1024 y
= aBBox
.Y() + ObjectSpace(aBBox
, &aXYWH
[1]);
1025 width
= ObjectSpace(aBBox
, &aXYWH
[2]);
1026 height
= ObjectSpace(aBBox
, &aXYWH
[3]);
1028 x
= UserSpace(aFrame
, &aXYWH
[0]);
1029 y
= UserSpace(aFrame
, &aXYWH
[1]);
1030 width
= UserSpace(aFrame
, &aXYWH
[2]);
1031 height
= UserSpace(aFrame
, &aXYWH
[3]);
1033 return gfxRect(x
, y
, width
, height
);
1037 nsSVGUtils::CanOptimizeOpacity(nsIFrame
*aFrame
)
1039 if (!(aFrame
->GetStateBits() & NS_FRAME_SVG_LAYOUT
)) {
1042 nsIAtom
*type
= aFrame
->GetType();
1043 if (type
!= nsGkAtoms::svgImageFrame
&&
1044 type
!= nsGkAtoms::svgPathGeometryFrame
) {
1047 if (aFrame
->StyleSVGReset()->HasFilters()) {
1050 // XXX The SVG WG is intending to allow fill, stroke and markers on <image>
1051 if (type
== nsGkAtoms::svgImageFrame
) {
1054 const nsStyleSVG
*style
= aFrame
->StyleSVG();
1055 if (style
->HasMarker()) {
1058 if (!style
->HasFill() || !HasStroke(aFrame
)) {
1065 nsSVGUtils::AdjustMatrixForUnits(const gfxMatrix
&aMatrix
,
1070 aUnits
->GetAnimValue() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
) {
1071 gfxRect bbox
= GetBBox(aFrame
);
1072 return gfxMatrix().Scale(bbox
.Width(), bbox
.Height()) *
1073 gfxMatrix().Translate(gfxPoint(bbox
.X(), bbox
.Y())) *
1080 nsSVGUtils::GetFirstNonAAncestorFrame(nsIFrame
* aStartFrame
)
1082 for (nsIFrame
*ancestorFrame
= aStartFrame
; ancestorFrame
;
1083 ancestorFrame
= ancestorFrame
->GetParent()) {
1084 if (ancestorFrame
->GetType() != nsGkAtoms::svgAFrame
) {
1085 return ancestorFrame
;
1092 nsSVGUtils::GetStrokeTransform(nsIFrame
*aFrame
)
1094 if (aFrame
->GetContent()->IsNodeOfType(nsINode::eTEXT
)) {
1095 aFrame
= aFrame
->GetParent();
1098 if (aFrame
->StyleSVGReset()->mVectorEffect
==
1099 NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE
) {
1101 nsIContent
*content
= aFrame
->GetContent();
1102 NS_ABORT_IF_FALSE(content
->IsSVG(), "bad cast");
1104 // a non-scaling stroke is in the screen co-ordinate
1105 // space rather so we need to invert the transform
1106 // to the screen co-ordinate space to get there.
1107 // See http://www.w3.org/TR/SVGTiny12/painting.html#NonScalingStroke
1108 gfx::Matrix transform
= SVGContentUtils::GetCTM(
1109 static_cast<nsSVGElement
*>(content
), true);
1110 if (!transform
.IsSingular()) {
1112 return ThebesMatrix(transform
);
1118 // The logic here comes from _cairo_stroke_style_max_distance_from_path
1120 PathExtentsToMaxStrokeExtents(const gfxRect
& aPathExtents
,
1122 double aStyleExpansionFactor
,
1123 const gfxMatrix
& aMatrix
)
1125 double style_expansion
=
1126 aStyleExpansionFactor
* nsSVGUtils::GetStrokeWidth(aFrame
);
1128 gfxMatrix matrix
= aMatrix
* nsSVGUtils::GetStrokeTransform(aFrame
);
1130 double dx
= style_expansion
* (fabs(matrix
._11
) + fabs(matrix
._21
));
1131 double dy
= style_expansion
* (fabs(matrix
._22
) + fabs(matrix
._12
));
1133 gfxRect strokeExtents
= aPathExtents
;
1134 strokeExtents
.Inflate(dx
, dy
);
1135 return strokeExtents
;
1139 nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect
& aPathExtents
,
1140 nsTextFrame
* aFrame
,
1141 const gfxMatrix
& aMatrix
)
1143 NS_ASSERTION(aFrame
->IsSVGText(), "expected an nsTextFrame for SVG text");
1144 return ::PathExtentsToMaxStrokeExtents(aPathExtents
, aFrame
, 0.5, aMatrix
);
1148 nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect
& aPathExtents
,
1149 nsSVGPathGeometryFrame
* aFrame
,
1150 const gfxMatrix
& aMatrix
)
1152 double styleExpansionFactor
= 0.5;
1154 if (static_cast<nsSVGPathGeometryElement
*>(aFrame
->GetContent())->IsMarkable()) {
1155 const nsStyleSVG
* style
= aFrame
->StyleSVG();
1157 if (style
->mStrokeLinecap
== NS_STYLE_STROKE_LINECAP_SQUARE
) {
1158 styleExpansionFactor
= M_SQRT1_2
;
1161 if (style
->mStrokeLinejoin
== NS_STYLE_STROKE_LINEJOIN_MITER
&&
1162 styleExpansionFactor
< style
->mStrokeMiterlimit
&&
1163 aFrame
->GetContent()->Tag() != nsGkAtoms::line
) {
1164 styleExpansionFactor
= style
->mStrokeMiterlimit
;
1168 return ::PathExtentsToMaxStrokeExtents(aPathExtents
,
1170 styleExpansionFactor
,
1174 // ----------------------------------------------------------------------
1176 /* static */ nscolor
1177 nsSVGUtils::GetFallbackOrPaintColor(gfxContext
*aContext
, nsStyleContext
*aStyleContext
,
1178 nsStyleSVGPaint
nsStyleSVG::*aFillOrStroke
)
1180 const nsStyleSVGPaint
&paint
= aStyleContext
->StyleSVG()->*aFillOrStroke
;
1181 nsStyleContext
*styleIfVisited
= aStyleContext
->GetStyleIfVisited();
1182 bool isServer
= paint
.mType
== eStyleSVGPaintType_Server
||
1183 paint
.mType
== eStyleSVGPaintType_ContextFill
||
1184 paint
.mType
== eStyleSVGPaintType_ContextStroke
;
1185 nscolor color
= isServer
? paint
.mFallbackColor
: paint
.mPaint
.mColor
;
1186 if (styleIfVisited
) {
1187 const nsStyleSVGPaint
&paintIfVisited
=
1188 styleIfVisited
->StyleSVG()->*aFillOrStroke
;
1189 // To prevent Web content from detecting if a user has visited a URL
1190 // (via URL loading triggered by paint servers or performance
1191 // differences between paint servers or between a paint server and a
1192 // color), we do not allow whether links are visited to change which
1193 // paint server is used or switch between paint servers and simple
1194 // colors. A :visited style may only override a simple color with
1195 // another simple color.
1196 if (paintIfVisited
.mType
== eStyleSVGPaintType_Color
&&
1197 paint
.mType
== eStyleSVGPaintType_Color
) {
1198 nscolor colors
[2] = { color
, paintIfVisited
.mPaint
.mColor
};
1199 return nsStyleContext::CombineVisitedColors(
1200 colors
, aStyleContext
->RelevantLinkVisited());
1207 SetupFallbackOrPaintColor(gfxContext
*aContext
, nsStyleContext
*aStyleContext
,
1208 nsStyleSVGPaint
nsStyleSVG::*aFillOrStroke
,
1211 nscolor color
= nsSVGUtils::GetFallbackOrPaintColor(
1212 aContext
, aStyleContext
, aFillOrStroke
);
1214 aContext
->SetColor(gfxRGBA(NS_GET_R(color
)/255.0,
1215 NS_GET_G(color
)/255.0,
1216 NS_GET_B(color
)/255.0,
1217 NS_GET_A(color
)/255.0 * aOpacity
));
1221 MaybeOptimizeOpacity(nsIFrame
*aFrame
, float aFillOrStrokeOpacity
)
1223 float opacity
= aFrame
->StyleDisplay()->mOpacity
;
1224 if (opacity
< 1 && nsSVGUtils::CanOptimizeOpacity(aFrame
)) {
1225 return aFillOrStrokeOpacity
* opacity
;
1227 return aFillOrStrokeOpacity
;
1231 nsSVGUtils::SetupContextPaint(gfxContext
*aContext
,
1232 gfxTextContextPaint
*aContextPaint
,
1233 const nsStyleSVGPaint
&aPaint
,
1236 nsRefPtr
<gfxPattern
> pattern
;
1238 if (!aContextPaint
) {
1242 switch (aPaint
.mType
) {
1243 case eStyleSVGPaintType_ContextFill
:
1244 pattern
= aContextPaint
->GetFillPattern(aOpacity
, aContext
->CurrentMatrix());
1246 case eStyleSVGPaintType_ContextStroke
:
1247 pattern
= aContextPaint
->GetStrokePattern(aOpacity
, aContext
->CurrentMatrix());
1257 aContext
->SetPattern(pattern
);
1263 nsSVGUtils::SetupCairoFillPaint(nsIFrame
*aFrame
, gfxContext
* aContext
,
1264 gfxTextContextPaint
*aContextPaint
)
1266 const nsStyleSVG
* style
= aFrame
->StyleSVG();
1267 if (style
->mFill
.mType
== eStyleSVGPaintType_None
)
1270 if (style
->mFillRule
== NS_STYLE_FILL_RULE_EVENODD
)
1271 aContext
->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD
);
1273 aContext
->SetFillRule(gfxContext::FILL_RULE_WINDING
);
1275 float opacity
= MaybeOptimizeOpacity(aFrame
,
1276 GetOpacity(style
->mFillOpacitySource
,
1277 style
->mFillOpacity
,
1279 nsSVGPaintServerFrame
*ps
=
1280 nsSVGEffects::GetPaintServer(aFrame
, &style
->mFill
, nsSVGEffects::FillProperty());
1281 if (ps
&& ps
->SetupPaintServer(aContext
, aFrame
, &nsStyleSVG::mFill
, opacity
))
1284 if (SetupContextPaint(aContext
, aContextPaint
, style
->mFill
, opacity
)) {
1288 // On failure, use the fallback colour in case we have an
1289 // objectBoundingBox where the width or height of the object is zero.
1290 // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
1291 SetupFallbackOrPaintColor(aContext
, aFrame
->StyleContext(),
1292 &nsStyleSVG::mFill
, opacity
);
1298 nsSVGUtils::SetupCairoStrokePaint(nsIFrame
*aFrame
, gfxContext
* aContext
,
1299 gfxTextContextPaint
*aContextPaint
)
1301 const nsStyleSVG
* style
= aFrame
->StyleSVG();
1302 if (style
->mStroke
.mType
== eStyleSVGPaintType_None
)
1305 float opacity
= MaybeOptimizeOpacity(aFrame
,
1306 GetOpacity(style
->mStrokeOpacitySource
,
1307 style
->mStrokeOpacity
,
1310 nsSVGPaintServerFrame
*ps
=
1311 nsSVGEffects::GetPaintServer(aFrame
, &style
->mStroke
, nsSVGEffects::StrokeProperty());
1312 if (ps
&& ps
->SetupPaintServer(aContext
, aFrame
, &nsStyleSVG::mStroke
, opacity
))
1315 if (SetupContextPaint(aContext
, aContextPaint
, style
->mStroke
, opacity
)) {
1319 // On failure, use the fallback colour in case we have an
1320 // objectBoundingBox where the width or height of the object is zero.
1321 // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
1322 SetupFallbackOrPaintColor(aContext
, aFrame
->StyleContext(),
1323 &nsStyleSVG::mStroke
, opacity
);
1329 nsSVGUtils::GetOpacity(nsStyleSVGOpacitySource aOpacityType
,
1330 const float& aOpacity
,
1331 gfxTextContextPaint
*aOuterContextPaint
)
1333 float opacity
= 1.0f
;
1334 switch (aOpacityType
) {
1335 case eStyleSVGOpacitySource_Normal
:
1338 case eStyleSVGOpacitySource_ContextFillOpacity
:
1339 if (aOuterContextPaint
) {
1340 opacity
= aOuterContextPaint
->GetFillOpacity();
1342 NS_WARNING("context-fill-opacity used outside of an SVG glyph");
1345 case eStyleSVGOpacitySource_ContextStrokeOpacity
:
1346 if (aOuterContextPaint
) {
1347 opacity
= aOuterContextPaint
->GetStrokeOpacity();
1349 NS_WARNING("context-stroke-opacity used outside of an SVG glyph");
1353 NS_NOTREACHED("Unknown object opacity inheritance type for SVG glyph");
1359 nsSVGUtils::HasStroke(nsIFrame
* aFrame
, gfxTextContextPaint
*aContextPaint
)
1361 const nsStyleSVG
*style
= aFrame
->StyleSVG();
1362 return style
->HasStroke() && GetStrokeWidth(aFrame
, aContextPaint
) > 0;
1366 nsSVGUtils::GetStrokeWidth(nsIFrame
* aFrame
, gfxTextContextPaint
*aContextPaint
)
1368 const nsStyleSVG
*style
= aFrame
->StyleSVG();
1369 if (aContextPaint
&& style
->mStrokeWidthFromObject
) {
1370 return aContextPaint
->GetStrokeWidth();
1373 nsIContent
* content
= aFrame
->GetContent();
1374 if (content
->IsNodeOfType(nsINode::eTEXT
)) {
1375 content
= content
->GetParent();
1378 nsSVGElement
*ctx
= static_cast<nsSVGElement
*>(content
);
1380 return SVGContentUtils::CoordToFloat(ctx
, style
->mStrokeWidth
);
1384 nsSVGUtils::SetupCairoStrokeBBoxGeometry(nsIFrame
* aFrame
,
1385 gfxContext
*aContext
,
1386 gfxTextContextPaint
*aContextPaint
)
1388 float width
= GetStrokeWidth(aFrame
, aContextPaint
);
1391 aContext
->SetLineWidth(width
);
1393 // Apply any stroke-specific transform
1394 gfxMatrix strokeTransform
= GetStrokeTransform(aFrame
);
1395 if (!strokeTransform
.IsIdentity()) {
1396 aContext
->Multiply(strokeTransform
);
1399 const nsStyleSVG
* style
= aFrame
->StyleSVG();
1401 switch (style
->mStrokeLinecap
) {
1402 case NS_STYLE_STROKE_LINECAP_BUTT
:
1403 aContext
->SetLineCap(gfxContext::LINE_CAP_BUTT
);
1405 case NS_STYLE_STROKE_LINECAP_ROUND
:
1406 aContext
->SetLineCap(gfxContext::LINE_CAP_ROUND
);
1408 case NS_STYLE_STROKE_LINECAP_SQUARE
:
1409 aContext
->SetLineCap(gfxContext::LINE_CAP_SQUARE
);
1413 aContext
->SetMiterLimit(style
->mStrokeMiterlimit
);
1415 switch (style
->mStrokeLinejoin
) {
1416 case NS_STYLE_STROKE_LINEJOIN_MITER
:
1417 aContext
->SetLineJoin(gfxContext::LINE_JOIN_MITER
);
1419 case NS_STYLE_STROKE_LINEJOIN_ROUND
:
1420 aContext
->SetLineJoin(gfxContext::LINE_JOIN_ROUND
);
1422 case NS_STYLE_STROKE_LINEJOIN_BEVEL
:
1423 aContext
->SetLineJoin(gfxContext::LINE_JOIN_BEVEL
);
1429 GetStrokeDashData(nsIFrame
* aFrame
,
1430 FallibleTArray
<gfxFloat
>& aDashes
,
1431 gfxFloat
* aDashOffset
,
1432 gfxTextContextPaint
*aContextPaint
)
1434 const nsStyleSVG
* style
= aFrame
->StyleSVG();
1435 nsIContent
*content
= aFrame
->GetContent();
1436 nsSVGElement
*ctx
= static_cast<nsSVGElement
*>
1437 (content
->IsNodeOfType(nsINode::eTEXT
) ?
1438 content
->GetParent() : content
);
1440 gfxFloat totalLength
= 0.0;
1441 if (aContextPaint
&& style
->mStrokeDasharrayFromObject
) {
1442 aDashes
= aContextPaint
->GetStrokeDashArray();
1444 for (uint32_t i
= 0; i
< aDashes
.Length(); i
++) {
1445 if (aDashes
[i
] < 0.0) {
1448 totalLength
+= aDashes
[i
];
1452 uint32_t count
= style
->mStrokeDasharrayLength
;
1453 if (!count
|| !aDashes
.SetLength(count
)) {
1457 gfxFloat pathScale
= 1.0;
1459 if (content
->Tag() == nsGkAtoms::path
) {
1460 pathScale
= static_cast<SVGPathElement
*>(content
)->
1461 GetPathLengthScale(SVGPathElement::eForStroking
);
1462 if (pathScale
<= 0) {
1467 const nsStyleCoord
*dasharray
= style
->mStrokeDasharray
;
1469 for (uint32_t i
= 0; i
< count
; i
++) {
1470 aDashes
[i
] = SVGContentUtils::CoordToFloat(ctx
,
1471 dasharray
[i
]) * pathScale
;
1472 if (aDashes
[i
] < 0.0) {
1475 totalLength
+= aDashes
[i
];
1479 if (aContextPaint
&& style
->mStrokeDashoffsetFromObject
) {
1480 *aDashOffset
= aContextPaint
->GetStrokeDashOffset();
1482 *aDashOffset
= SVGContentUtils::CoordToFloat(ctx
,
1483 style
->mStrokeDashoffset
);
1486 return (totalLength
> 0.0);
1490 nsSVGUtils::SetupCairoStrokeGeometry(nsIFrame
* aFrame
, gfxContext
* aContext
,
1491 gfxTextContextPaint
*aContextPaint
)
1493 SetupCairoStrokeBBoxGeometry(aFrame
, aContext
, aContextPaint
);
1495 AutoFallibleTArray
<gfxFloat
, 10> dashes
;
1496 gfxFloat dashOffset
;
1497 if (GetStrokeDashData(aFrame
, dashes
, &dashOffset
, aContextPaint
)) {
1498 aContext
->SetDash(dashes
.Elements(), dashes
.Length(), dashOffset
);
1503 nsSVGUtils::GetGeometryHitTestFlags(nsIFrame
* aFrame
)
1507 switch(aFrame
->StyleVisibility()->mPointerEvents
) {
1508 case NS_STYLE_POINTER_EVENTS_NONE
:
1510 case NS_STYLE_POINTER_EVENTS_AUTO
:
1511 case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED
:
1512 if (aFrame
->StyleVisibility()->IsVisible()) {
1513 if (aFrame
->StyleSVG()->mFill
.mType
!= eStyleSVGPaintType_None
)
1514 flags
|= SVG_HIT_TEST_FILL
;
1515 if (aFrame
->StyleSVG()->mStroke
.mType
!= eStyleSVGPaintType_None
)
1516 flags
|= SVG_HIT_TEST_STROKE
;
1517 if (aFrame
->StyleSVG()->mStrokeOpacity
> 0)
1518 flags
|= SVG_HIT_TEST_CHECK_MRECT
;
1521 case NS_STYLE_POINTER_EVENTS_VISIBLEFILL
:
1522 if (aFrame
->StyleVisibility()->IsVisible()) {
1523 flags
|= SVG_HIT_TEST_FILL
;
1526 case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE
:
1527 if (aFrame
->StyleVisibility()->IsVisible()) {
1528 flags
|= SVG_HIT_TEST_STROKE
;
1531 case NS_STYLE_POINTER_EVENTS_VISIBLE
:
1532 if (aFrame
->StyleVisibility()->IsVisible()) {
1533 flags
|= SVG_HIT_TEST_FILL
| SVG_HIT_TEST_STROKE
;
1536 case NS_STYLE_POINTER_EVENTS_PAINTED
:
1537 if (aFrame
->StyleSVG()->mFill
.mType
!= eStyleSVGPaintType_None
)
1538 flags
|= SVG_HIT_TEST_FILL
;
1539 if (aFrame
->StyleSVG()->mStroke
.mType
!= eStyleSVGPaintType_None
)
1540 flags
|= SVG_HIT_TEST_STROKE
;
1541 if (aFrame
->StyleSVG()->mStrokeOpacity
)
1542 flags
|= SVG_HIT_TEST_CHECK_MRECT
;
1544 case NS_STYLE_POINTER_EVENTS_FILL
:
1545 flags
|= SVG_HIT_TEST_FILL
;
1547 case NS_STYLE_POINTER_EVENTS_STROKE
:
1548 flags
|= SVG_HIT_TEST_STROKE
;
1550 case NS_STYLE_POINTER_EVENTS_ALL
:
1551 flags
|= SVG_HIT_TEST_FILL
| SVG_HIT_TEST_STROKE
;
1554 NS_ERROR("not reached");
1562 nsSVGUtils::SetupCairoStroke(nsIFrame
* aFrame
, gfxContext
* aContext
,
1563 gfxTextContextPaint
*aContextPaint
)
1565 if (!HasStroke(aFrame
, aContextPaint
)) {
1568 SetupCairoStrokeGeometry(aFrame
, aContext
, aContextPaint
);
1570 return SetupCairoStrokePaint(aFrame
, aContext
, aContextPaint
);
1574 nsSVGUtils::PaintSVGGlyph(Element
* aElement
, gfxContext
* aContext
,
1576 gfxTextContextPaint
* aContextPaint
)
1578 nsIFrame
* frame
= aElement
->GetPrimaryFrame();
1579 nsISVGChildFrame
* svgFrame
= do_QueryFrame(frame
);
1583 nsRefPtr
<nsRenderingContext
> context(new nsRenderingContext());
1584 context
->Init(frame
->PresContext()->DeviceContext(), aContext
);
1585 context
->AddUserData(&gfxTextContextPaint::sUserDataKey
, aContextPaint
,
1587 svgFrame
->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED
);
1588 nsresult rv
= svgFrame
->PaintSVG(context
, nullptr, frame
);
1589 return NS_SUCCEEDED(rv
);
1593 nsSVGUtils::GetSVGGlyphExtents(Element
* aElement
,
1594 const gfxMatrix
& aSVGToAppSpace
,
1597 nsIFrame
* frame
= aElement
->GetPrimaryFrame();
1598 nsISVGChildFrame
* svgFrame
= do_QueryFrame(frame
);
1603 gfxMatrix
transform(aSVGToAppSpace
);
1604 nsIContent
* content
= frame
->GetContent();
1605 if (content
->IsSVG()) {
1606 transform
= static_cast<nsSVGElement
*>(content
)->
1607 PrependLocalTransformsTo(aSVGToAppSpace
);
1610 *aResult
= svgFrame
->GetBBoxContribution(gfx::ToMatrix(transform
),
1611 nsSVGUtils::eBBoxIncludeFill
| nsSVGUtils::eBBoxIncludeFillGeometry
|
1612 nsSVGUtils::eBBoxIncludeStroke
| nsSVGUtils::eBBoxIncludeStrokeGeometry
|
1613 nsSVGUtils::eBBoxIncludeMarkers
).ToThebesRect();
1618 nsSVGUtils::ToCanvasBounds(const gfxRect
&aUserspaceRect
,
1619 const gfxMatrix
&aToCanvas
,
1620 const nsPresContext
*presContext
)
1622 return nsLayoutUtils::RoundGfxRectToAppRect(
1623 aToCanvas
.TransformBounds(aUserspaceRect
),
1624 presContext
->AppUnitsPerDevPixel());