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 #include "nsSVGPathGeometryFrame.h"
9 // Keep others in (case-insensitive) order:
10 #include "gfx2DGlue.h"
11 #include "gfxContext.h"
12 #include "gfxPlatform.h"
13 #include "gfxSVGGlyphs.h"
14 #include "mozilla/gfx/2D.h"
15 #include "mozilla/RefPtr.h"
16 #include "nsDisplayList.h"
17 #include "nsGkAtoms.h"
18 #include "nsLayoutUtils.h"
19 #include "nsRenderingContext.h"
20 #include "nsSVGEffects.h"
21 #include "nsSVGIntegrationUtils.h"
22 #include "nsSVGMarkerFrame.h"
23 #include "nsSVGPathGeometryElement.h"
24 #include "nsSVGUtils.h"
25 #include "mozilla/ArrayUtils.h"
26 #include "SVGAnimatedTransformList.h"
27 #include "SVGContentUtils.h"
28 #include "SVGGraphicsElement.h"
30 using namespace mozilla
;
31 using namespace mozilla::gfx
;
33 //----------------------------------------------------------------------
37 NS_NewSVGPathGeometryFrame(nsIPresShell
* aPresShell
,
38 nsStyleContext
* aContext
)
40 return new (aPresShell
) nsSVGPathGeometryFrame(aContext
);
43 NS_IMPL_FRAMEARENA_HELPERS(nsSVGPathGeometryFrame
)
45 //----------------------------------------------------------------------
46 // nsQueryFrame methods
48 NS_QUERYFRAME_HEAD(nsSVGPathGeometryFrame
)
49 NS_QUERYFRAME_ENTRY(nsISVGChildFrame
)
50 NS_QUERYFRAME_ENTRY(nsSVGPathGeometryFrame
)
51 NS_QUERYFRAME_TAIL_INHERITING(nsSVGPathGeometryFrameBase
)
53 //----------------------------------------------------------------------
56 class nsDisplaySVGPathGeometry
: public nsDisplayItem
{
58 nsDisplaySVGPathGeometry(nsDisplayListBuilder
* aBuilder
,
59 nsSVGPathGeometryFrame
* aFrame
)
60 : nsDisplayItem(aBuilder
, aFrame
)
62 MOZ_COUNT_CTOR(nsDisplaySVGPathGeometry
);
63 NS_ABORT_IF_FALSE(aFrame
, "Must have a frame!");
65 #ifdef NS_BUILD_REFCNT_LOGGING
66 virtual ~nsDisplaySVGPathGeometry() {
67 MOZ_COUNT_DTOR(nsDisplaySVGPathGeometry
);
71 NS_DISPLAY_DECL_NAME("nsDisplaySVGPathGeometry", TYPE_SVG_PATH_GEOMETRY
)
73 virtual void HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
74 HitTestState
* aState
, nsTArray
<nsIFrame
*> *aOutFrames
);
75 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
76 nsRenderingContext
* aCtx
);
80 nsDisplaySVGPathGeometry::HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
81 HitTestState
* aState
, nsTArray
<nsIFrame
*> *aOutFrames
)
83 nsSVGPathGeometryFrame
*frame
= static_cast<nsSVGPathGeometryFrame
*>(mFrame
);
84 nsPoint pointRelativeToReferenceFrame
= aRect
.Center();
85 // ToReferenceFrame() includes frame->GetPosition(), our user space position.
86 nsPoint userSpacePtInAppUnits
= pointRelativeToReferenceFrame
-
87 (ToReferenceFrame() - frame
->GetPosition());
88 gfxPoint userSpacePt
=
89 gfxPoint(userSpacePtInAppUnits
.x
, userSpacePtInAppUnits
.y
) /
90 frame
->PresContext()->AppUnitsPerCSSPixel();
91 if (frame
->GetFrameForPoint(userSpacePt
)) {
92 aOutFrames
->AppendElement(frame
);
97 nsDisplaySVGPathGeometry::Paint(nsDisplayListBuilder
* aBuilder
,
98 nsRenderingContext
* aCtx
)
100 // ToReferenceFrame includes our mRect offset, but painting takes
101 // account of that too. To avoid double counting, we subtract that
103 nsPoint offset
= ToReferenceFrame() - mFrame
->GetPosition();
106 aCtx
->Translate(offset
);
107 static_cast<nsSVGPathGeometryFrame
*>(mFrame
)->PaintSVG(aCtx
, nullptr);
111 //----------------------------------------------------------------------
115 nsSVGPathGeometryFrame::Init(nsIContent
* aContent
,
116 nsContainerFrame
* aParent
,
117 nsIFrame
* aPrevInFlow
)
119 AddStateBits(aParent
->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD
);
120 nsSVGPathGeometryFrameBase::Init(aContent
, aParent
, aPrevInFlow
);
124 nsSVGPathGeometryFrame::AttributeChanged(int32_t aNameSpaceID
,
128 // We don't invalidate for transform changes (the layers code does that).
129 // Also note that SVGTransformableElement::GetAttributeChangeHint will
130 // return nsChangeHint_UpdateOverflow for "transform" attribute changes
131 // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
133 if (aNameSpaceID
== kNameSpaceID_None
&&
134 (static_cast<nsSVGPathGeometryElement
*>
135 (mContent
)->AttributeDefinesGeometry(aAttribute
))) {
136 nsSVGEffects::InvalidateRenderingObservers(this);
137 nsSVGUtils::ScheduleReflowSVG(this);
143 nsSVGPathGeometryFrame::DidSetStyleContext(nsStyleContext
* aOldStyleContext
)
145 nsSVGPathGeometryFrameBase::DidSetStyleContext(aOldStyleContext
);
147 if (aOldStyleContext
) {
148 float oldOpacity
= aOldStyleContext
->PeekStyleDisplay()->mOpacity
;
149 float newOpacity
= StyleDisplay()->mOpacity
;
150 if (newOpacity
!= oldOpacity
&&
151 nsSVGUtils::CanOptimizeOpacity(this)) {
152 // nsIFrame::BuildDisplayListForStackingContext() is not going to create an
153 // nsDisplayOpacity display list item, so DLBI won't invalidate for us.
160 nsSVGPathGeometryFrame::GetType() const
162 return nsGkAtoms::svgPathGeometryFrame
;
166 nsSVGPathGeometryFrame::IsSVGTransformed(gfx::Matrix
*aOwnTransform
,
167 gfx::Matrix
*aFromParentTransform
) const
169 bool foundTransform
= false;
171 // Check if our parent has children-only transforms:
172 nsIFrame
*parent
= GetParent();
174 parent
->IsFrameOfType(nsIFrame::eSVG
| nsIFrame::eSVGContainer
)) {
175 foundTransform
= static_cast<nsSVGContainerFrame
*>(parent
)->
176 HasChildrenOnlyTransform(aFromParentTransform
);
179 nsSVGElement
*content
= static_cast<nsSVGElement
*>(mContent
);
180 nsSVGAnimatedTransformList
* transformList
=
181 content
->GetAnimatedTransformList();
182 if ((transformList
&& transformList
->HasTransform()) ||
183 content
->GetAnimateMotionTransform()) {
185 *aOwnTransform
= gfx::ToMatrix(content
->PrependLocalTransformsTo(gfxMatrix(),
186 nsSVGElement::eUserSpaceToParent
));
188 foundTransform
= true;
190 return foundTransform
;
194 nsSVGPathGeometryFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
195 const nsRect
& aDirtyRect
,
196 const nsDisplayListSet
& aLists
)
198 if (!static_cast<const nsSVGElement
*>(mContent
)->HasValidDimensions()) {
201 aLists
.Content()->AppendNewToTop(
202 new (aBuilder
) nsDisplaySVGPathGeometry(aBuilder
, this));
205 //----------------------------------------------------------------------
206 // nsISVGChildFrame methods
209 nsSVGPathGeometryFrame::PaintSVG(nsRenderingContext
*aContext
,
210 const nsIntRect
*aDirtyRect
,
211 nsIFrame
* aTransformRoot
)
213 if (!StyleVisibility()->IsVisible())
216 uint32_t paintOrder
= StyleSVG()->mPaintOrder
;
217 if (paintOrder
== NS_STYLE_PAINT_ORDER_NORMAL
) {
218 Render(aContext
, eRenderFill
| eRenderStroke
, aTransformRoot
);
219 PaintMarkers(aContext
);
223 paintOrder
& ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH
) - 1);
225 case NS_STYLE_PAINT_ORDER_FILL
:
226 Render(aContext
, eRenderFill
, aTransformRoot
);
228 case NS_STYLE_PAINT_ORDER_STROKE
:
229 Render(aContext
, eRenderStroke
, aTransformRoot
);
231 case NS_STYLE_PAINT_ORDER_MARKERS
:
232 PaintMarkers(aContext
);
235 paintOrder
>>= NS_STYLE_PAINT_ORDER_BITWIDTH
;
243 nsSVGPathGeometryFrame::GetFrameForPoint(const gfxPoint
& aPoint
)
246 uint16_t hitTestFlags
;
247 if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD
) {
248 hitTestFlags
= SVG_HIT_TEST_FILL
;
249 fillRule
= StyleSVG()->mClipRule
== NS_STYLE_FILL_RULE_NONZERO
250 ? FillRule::FILL_WINDING
: FillRule::FILL_EVEN_ODD
;
252 hitTestFlags
= GetHitTestFlags();
256 if (hitTestFlags
& SVG_HIT_TEST_CHECK_MRECT
) {
258 nsLayoutUtils::RectToGfxRect(mRect
, PresContext()->AppUnitsPerCSSPixel());
259 if (!rect
.Contains(aPoint
)) {
263 fillRule
= StyleSVG()->mFillRule
== NS_STYLE_FILL_RULE_NONZERO
264 ? FillRule::FILL_WINDING
: FillRule::FILL_EVEN_ODD
;
269 nsSVGPathGeometryElement
* content
=
270 static_cast<nsSVGPathGeometryElement
*>(mContent
);
272 // Using ScreenReferenceDrawTarget() opens us to Moz2D backend specific hit-
273 // testing bugs. Maybe we should use a BackendType::CAIRO DT for hit-testing
274 // so that we get more consistent/backwards compatible results?
275 RefPtr
<DrawTarget
> drawTarget
=
276 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
277 RefPtr
<PathBuilder
> builder
=
278 drawTarget
->CreatePathBuilder(fillRule
);
279 RefPtr
<Path
> path
= content
->BuildPath(builder
);
281 return nullptr; // no path, so we don't paint anything that can be hit
284 if (hitTestFlags
& SVG_HIT_TEST_FILL
) {
285 isHit
= path
->ContainsPoint(ToPoint(aPoint
), Matrix());
287 if (!isHit
&& (hitTestFlags
& SVG_HIT_TEST_STROKE
)) {
288 Point point
= ToPoint(aPoint
);
289 SVGContentUtils::AutoStrokeOptions stroke
;
290 SVGContentUtils::GetStrokeOptions(&stroke
, content
, StyleContext(), nullptr);
291 Matrix nonScalingStrokeMatrix
= ToMatrix(nsSVGUtils::GetStrokeTransform(this));
292 if (!nonScalingStrokeMatrix
.IsIdentity()) {
293 // We need to transform the path back into the appropriate ancestor
294 // coordinate system in order for non-scaled stroke to be correct.
295 // Naturally we also need to transform the point into the same
296 // coordinate system in order to hit-test against the path.
297 if (!nonScalingStrokeMatrix
.Invert()) {
300 point
= nonScalingStrokeMatrix
* point
;
301 RefPtr
<PathBuilder
> builder
=
302 path
->TransformedCopyToBuilder(nonScalingStrokeMatrix
, fillRule
);
303 path
= builder
->Finish();
305 isHit
= path
->StrokeContainsPoint(stroke
, point
, Matrix());
308 if (isHit
&& nsSVGUtils::HitTestClip(this, aPoint
))
315 nsSVGPathGeometryFrame::GetCoveredRegion()
317 return nsSVGUtils::TransformFrameRectToOuterSVG(
318 mRect
, GetCanvasTM(FOR_OUTERSVG_TM
), PresContext());
322 nsSVGPathGeometryFrame::ReflowSVG()
324 NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
325 "This call is probably a wasteful mistake");
327 NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY
),
328 "ReflowSVG mechanism not designed for this");
330 if (!nsSVGUtils::NeedsReflowSVG(this)) {
334 uint32_t flags
= nsSVGUtils::eBBoxIncludeFill
|
335 nsSVGUtils::eBBoxIncludeStroke
|
336 nsSVGUtils::eBBoxIncludeMarkers
;
337 // Our "visual" overflow rect needs to be valid for building display lists
338 // for hit testing, which means that for certain values of 'pointer-events'
339 // it needs to include the geometry of the fill or stroke even when the fill/
340 // stroke don't actually render (e.g. when stroke="none" or
341 // stroke-opacity="0"). GetHitTestFlags() accounts for 'pointer-events'.
342 uint16_t hitTestFlags
= GetHitTestFlags();
343 if ((hitTestFlags
& SVG_HIT_TEST_FILL
)) {
344 flags
|= nsSVGUtils::eBBoxIncludeFillGeometry
;
346 if ((hitTestFlags
& SVG_HIT_TEST_STROKE
)) {
347 flags
|= nsSVGUtils::eBBoxIncludeStrokeGeometry
;
350 // We'd like to just pass the identity matrix to GetBBoxContribution, but if
351 // this frame's user space size is _very_ large/small then the extents we
352 // obtain below might have overflowed or otherwise be broken. This would
353 // cause us to end up with a broken mRect and visual overflow rect and break
354 // painting of this frame. This is particularly noticeable if the transforms
355 // between us and our nsSVGOuterSVGFrame scale this frame to a reasonable
356 // size. To avoid this we sadly have to do extra work to account for the
357 // transforms between us and our nsSVGOuterSVGFrame, even though the
358 // overwhelming number of SVGs will never have this problem.
359 // XXX Will Azure eventually save us from having to do this?
360 gfxSize scaleFactors
= GetCanvasTM(FOR_OUTERSVG_TM
).ScaleFactors(true);
361 bool applyScaling
= fabs(scaleFactors
.width
) >= 1e-6 &&
362 fabs(scaleFactors
.height
) >= 1e-6;
365 scaling
.Scale(scaleFactors
.width
, scaleFactors
.height
);
367 gfxRect extent
= GetBBoxContribution(scaling
, flags
).ToThebesRect();
369 extent
.Scale(1 / scaleFactors
.width
, 1 / scaleFactors
.height
);
371 mRect
= nsLayoutUtils::RoundGfxRectToAppRect(extent
,
372 PresContext()->AppUnitsPerCSSPixel());
374 if (mState
& NS_FRAME_FIRST_REFLOW
) {
375 // Make sure we have our filter property (if any) before calling
376 // FinishAndStoreOverflow (subsequent filter changes are handled off
377 // nsChangeHint_UpdateEffects):
378 nsSVGEffects::UpdateEffects(this);
381 nsRect overflow
= nsRect(nsPoint(0,0), mRect
.Size());
382 nsOverflowAreas
overflowAreas(overflow
, overflow
);
383 FinishAndStoreOverflow(overflowAreas
, mRect
.Size());
385 mState
&= ~(NS_FRAME_FIRST_REFLOW
| NS_FRAME_IS_DIRTY
|
386 NS_FRAME_HAS_DIRTY_CHILDREN
);
388 // Invalidate, but only if this is not our first reflow (since if it is our
389 // first reflow then we haven't had our first paint yet).
390 if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW
)) {
396 nsSVGPathGeometryFrame::NotifySVGChanged(uint32_t aFlags
)
398 NS_ABORT_IF_FALSE(aFlags
& (TRANSFORM_CHANGED
| COORD_CONTEXT_CHANGED
),
399 "Invalidation logic may need adjusting");
401 // Changes to our ancestors may affect how we render when we are rendered as
402 // part of our ancestor (specifically, if our coordinate context changes size
403 // and we have percentage lengths defining our geometry, then we need to be
404 // reflowed). However, ancestor changes cannot affect how we render when we
405 // are rendered as part of any rendering observers that we may have.
406 // Therefore no need to notify rendering observers here.
408 // Don't try to be too smart trying to avoid the ScheduleReflowSVG calls
409 // for the stroke properties examined below. Checking HasStroke() is not
410 // enough, since what we care about is whether we include the stroke in our
411 // overflow rects or not, and we sometimes deliberately include stroke
412 // when it's not visible. See the complexities of GetBBoxContribution.
414 if (aFlags
& COORD_CONTEXT_CHANGED
) {
415 // Stroke currently contributes to our mRect, which is why we have to take
416 // account of stroke-width here. Note that we do not need to take account
417 // of stroke-dashoffset since, although that can have a percentage value
418 // that is resolved against our coordinate context, it does not affect our
420 if (static_cast<nsSVGPathGeometryElement
*>(mContent
)->GeometryDependsOnCoordCtx() ||
421 StyleSVG()->mStrokeWidth
.HasPercent()) {
422 nsSVGUtils::ScheduleReflowSVG(this);
426 if ((aFlags
& TRANSFORM_CHANGED
) &&
427 StyleSVGReset()->mVectorEffect
==
428 NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE
) {
429 // Stroke currently contributes to our mRect, and our stroke depends on
430 // the transform to our outer-<svg> if |vector-effect:non-scaling-stroke|.
431 nsSVGUtils::ScheduleReflowSVG(this);
436 nsSVGPathGeometryFrame::GetBBoxContribution(const Matrix
&aToBBoxUserspace
,
441 if (aToBBoxUserspace
.IsSingular()) {
442 // XXX ReportToConsole
446 RefPtr
<DrawTarget
> tmpDT
;
448 // Unfortunately D2D backed DrawTarget produces bounds with rounding errors
449 // when whole number results are expected, even in the case of trivial
450 // calculations. To avoid that and meet the expectations of web content we
451 // have to use a CAIRO DrawTarget. The most efficient way to do that is to
452 // wrap the cached cairo_surface_t from ScreenReferenceSurface():
453 nsRefPtr
<gfxASurface
> refSurf
=
454 gfxPlatform::GetPlatform()->ScreenReferenceSurface();
455 tmpDT
= gfxPlatform::GetPlatform()->
456 CreateDrawTargetForSurface(refSurf
, IntSize(1, 1));
458 tmpDT
= gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
460 nsRefPtr
<gfxContext
> tmpCtx
= new gfxContext(tmpDT
);
462 GeneratePath(tmpCtx
, aToBBoxUserspace
);
463 tmpCtx
->IdentityMatrix();
465 // Be careful when replacing the following logic to get the fill and stroke
466 // extents independently (instead of computing the stroke extents from the
467 // path extents). You may think that you can just use the stroke extents if
468 // there is both a fill and a stroke. In reality it's necessary to calculate
469 // both the fill and stroke extents, and take the union of the two. There are
470 // two reasons for this:
472 // # Due to stroke dashing, in certain cases the fill extents could actually
473 // extend outside the stroke extents.
474 // # If the stroke is very thin, cairo won't paint any stroke, and so the
475 // stroke bounds that it will return will be empty.
477 gfxRect pathExtents
= tmpCtx
->GetUserPathExtent();
480 if ((aFlags
& nsSVGUtils::eBBoxIncludeFillGeometry
) ||
481 ((aFlags
& nsSVGUtils::eBBoxIncludeFill
) &&
482 StyleSVG()->mFill
.mType
!= eStyleSVGPaintType_None
)) {
486 // Account for stroke:
487 if ((aFlags
& nsSVGUtils::eBBoxIncludeStrokeGeometry
) ||
488 ((aFlags
& nsSVGUtils::eBBoxIncludeStroke
) &&
489 nsSVGUtils::HasStroke(this))) {
490 // We can't use tmpCtx->GetUserStrokeExtent() since it doesn't work for
491 // device space extents. Instead we approximate the stroke extents from
492 // pathExtents using PathExtentsToMaxStrokeExtents.
493 if (pathExtents
.Width() <= 0 && pathExtents
.Height() <= 0) {
494 // We have a zero length path, but it may still have non-empty stroke
495 // bounds depending on the value of stroke-linecap. We need to fix up
496 // pathExtents before it can be used with PathExtentsToMaxStrokeExtents
497 // though, because if pathExtents is empty, its position will not have
498 // been set. Happily we can use tmpCtx->GetUserStrokeExtent() to find
499 // the center point of the extents even though it gets the extents wrong.
500 nsSVGUtils::SetupCairoStrokeBBoxGeometry(this, tmpCtx
);
501 pathExtents
.MoveTo(tmpCtx
->GetUserStrokeExtent().Center());
502 pathExtents
.SizeTo(0, 0);
504 bbox
.UnionEdges(nsSVGUtils::PathExtentsToMaxStrokeExtents(pathExtents
,
506 ThebesMatrix(aToBBoxUserspace
)));
509 // Account for markers:
510 if ((aFlags
& nsSVGUtils::eBBoxIncludeMarkers
) != 0 &&
511 static_cast<nsSVGPathGeometryElement
*>(mContent
)->IsMarkable()) {
513 float strokeWidth
= nsSVGUtils::GetStrokeWidth(this);
514 MarkerProperties properties
= GetMarkerProperties(this);
516 if (properties
.MarkersExist()) {
517 nsTArray
<nsSVGMark
> marks
;
518 static_cast<nsSVGPathGeometryElement
*>(mContent
)->GetMarkPoints(&marks
);
519 uint32_t num
= marks
.Length();
521 // These are in the same order as the nsSVGMark::Type constants.
522 nsSVGMarkerFrame
* markerFrames
[] = {
523 properties
.GetMarkerStartFrame(),
524 properties
.GetMarkerMidFrame(),
525 properties
.GetMarkerEndFrame(),
527 PR_STATIC_ASSERT(MOZ_ARRAY_LENGTH(markerFrames
) == nsSVGMark::eTypeCount
);
529 for (uint32_t i
= 0; i
< num
; i
++) {
530 nsSVGMark
& mark
= marks
[i
];
531 nsSVGMarkerFrame
* frame
= markerFrames
[mark
.type
];
534 frame
->GetMarkBBoxContribution(aToBBoxUserspace
, aFlags
, this,
535 &marks
[i
], strokeWidth
);
536 bbox
.UnionEdges(mbbox
);
545 //----------------------------------------------------------------------
546 // nsSVGPathGeometryFrame methods:
549 nsSVGPathGeometryFrame::GetCanvasTM(uint32_t aFor
, nsIFrame
* aTransformRoot
)
551 if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY
) && !aTransformRoot
) {
552 if (aFor
== FOR_PAINTING
&& NS_SVGDisplayListPaintingEnabled()) {
553 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this);
557 NS_ASSERTION(GetParent(), "null parent");
559 nsSVGContainerFrame
*parent
= static_cast<nsSVGContainerFrame
*>(GetParent());
560 dom::SVGGraphicsElement
*content
= static_cast<dom::SVGGraphicsElement
*>(mContent
);
562 return content
->PrependLocalTransformsTo(
563 this == aTransformRoot
? gfxMatrix() :
564 parent
->GetCanvasTM(aFor
, aTransformRoot
));
567 nsSVGPathGeometryFrame::MarkerProperties
568 nsSVGPathGeometryFrame::GetMarkerProperties(nsSVGPathGeometryFrame
*aFrame
)
570 NS_ASSERTION(!aFrame
->GetPrevContinuation(), "aFrame should be first continuation");
572 MarkerProperties result
;
573 const nsStyleSVG
*style
= aFrame
->StyleSVG();
574 result
.mMarkerStart
=
575 nsSVGEffects::GetMarkerProperty(style
->mMarkerStart
, aFrame
,
576 nsSVGEffects::MarkerBeginProperty());
578 nsSVGEffects::GetMarkerProperty(style
->mMarkerMid
, aFrame
,
579 nsSVGEffects::MarkerMiddleProperty());
581 nsSVGEffects::GetMarkerProperty(style
->mMarkerEnd
, aFrame
,
582 nsSVGEffects::MarkerEndProperty());
587 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerStartFrame()
591 return static_cast<nsSVGMarkerFrame
*>
592 (mMarkerStart
->GetReferencedFrame(nsGkAtoms::svgMarkerFrame
, nullptr));
596 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerMidFrame()
600 return static_cast<nsSVGMarkerFrame
*>
601 (mMarkerMid
->GetReferencedFrame(nsGkAtoms::svgMarkerFrame
, nullptr));
605 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerEndFrame()
609 return static_cast<nsSVGMarkerFrame
*>
610 (mMarkerEnd
->GetReferencedFrame(nsGkAtoms::svgMarkerFrame
, nullptr));
614 nsSVGPathGeometryFrame::Render(nsRenderingContext
*aContext
,
615 uint32_t aRenderComponents
,
616 nsIFrame
* aTransformRoot
)
618 gfxContext
*gfx
= aContext
->ThebesContext();
620 uint16_t renderMode
= SVGAutoRenderState::GetRenderMode(aContext
);
622 switch (StyleSVG()->mShapeRendering
) {
623 case NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED
:
624 case NS_STYLE_SHAPE_RENDERING_CRISPEDGES
:
625 gfx
->SetAntialiasMode(gfxContext::MODE_ALIASED
);
628 gfx
->SetAntialiasMode(gfxContext::MODE_COVERAGE
);
632 if (renderMode
!= SVGAutoRenderState::NORMAL
) {
633 NS_ABORT_IF_FALSE(renderMode
== SVGAutoRenderState::CLIP
||
634 renderMode
== SVGAutoRenderState::CLIP_MASK
,
635 "Unknown render mode");
637 // In the case that |renderMode == SVGAutoRenderState::CLIP| then we don't
638 // use the path we generate here until further up the call stack when
639 // nsSVGClipPathFrame::Clip calls gfxContext::Clip. That's a problem for
640 // Moz2D which emits paths in user space (unlike cairo which emits paths in
641 // device space). gfxContext has hacks to deal with code changing the
642 // transform then using the current path when it is backed by Moz2D, but
643 // Moz2D itself does not since that would fundamentally go against its API.
644 // Therefore we do not want to Save()/Restore() the gfxContext here in the
645 // SVGAutoRenderState::CLIP case since that would block us from killing off
646 // gfxContext and using Moz2D directly. Not bothering to Save()/Restore()
647 // is actually okay, since we know that doesn't matter in the
648 // SVGAutoRenderState::CLIP case (at least for the current implementation).
649 gfxContextMatrixAutoSaveRestore autoSaveRestore
;
650 // For now revent back to doing the save even for CLIP to fix bug 959128.
651 // Undo in bug 987193.
652 //if (renderMode != SVGAutoRenderState::CLIP) {
653 autoSaveRestore
.SetContext(gfx
);
656 GeneratePath(gfx
, ToMatrix(GetCanvasTM(FOR_PAINTING
, aTransformRoot
)));
658 // We used to call gfx->Restore() here, since for the
659 // SVGAutoRenderState::CLIP case it is important to leave the fill rule
660 // that we set below untouched so that the value is still set when return
661 // to gfxContext::Clip() further up the call stack. Since we no longer
662 // call gfx->Save() in the SVGAutoRenderState::CLIP case we don't need to
663 // worry that autoSaveRestore will delay the Restore() call for the
664 // CLIP_MASK case until we exit this function.
666 gfxContext::FillRule oldFillRull
= gfx
->CurrentFillRule();
668 if (StyleSVG()->mClipRule
== NS_STYLE_FILL_RULE_EVENODD
)
669 gfx
->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD
);
671 gfx
->SetFillRule(gfxContext::FILL_RULE_WINDING
);
673 if (renderMode
== SVGAutoRenderState::CLIP_MASK
) {
674 gfx
->SetColor(gfxRGBA(1.0f
, 1.0f
, 1.0f
, 1.0f
));
676 gfx
->SetFillRule(oldFillRull
); // restore, but only for CLIP_MASK
683 gfxContextAutoSaveRestore
autoSaveRestore(gfx
);
685 GeneratePath(gfx
, ToMatrix(GetCanvasTM(FOR_PAINTING
, aTransformRoot
)));
687 gfxTextContextPaint
*contextPaint
=
688 (gfxTextContextPaint
*)aContext
->GetUserData(&gfxTextContextPaint::sUserDataKey
);
690 if ((aRenderComponents
& eRenderFill
) &&
691 nsSVGUtils::SetupCairoFillPaint(this, gfx
, contextPaint
)) {
695 if ((aRenderComponents
& eRenderStroke
) &&
696 nsSVGUtils::SetupCairoStroke(this, gfx
, contextPaint
)) {
704 nsSVGPathGeometryFrame::GeneratePath(gfxContext
* aContext
,
705 const Matrix
&aTransform
)
707 if (aTransform
.IsSingular()) {
708 aContext
->IdentityMatrix();
713 aContext
->MultiplyAndNudgeToIntegers(ThebesMatrix(aTransform
));
715 // Hack to let SVGPathData::ConstructPath know if we have square caps:
716 const nsStyleSVG
* style
= StyleSVG();
717 if (style
->mStrokeLinecap
== NS_STYLE_STROKE_LINECAP_SQUARE
) {
718 aContext
->SetLineCap(gfxContext::LINE_CAP_SQUARE
);
722 static_cast<nsSVGPathGeometryElement
*>(mContent
)->ConstructPath(aContext
);
726 nsSVGPathGeometryFrame::PaintMarkers(nsRenderingContext
* aContext
)
728 gfxTextContextPaint
*contextPaint
=
729 (gfxTextContextPaint
*)aContext
->GetUserData(&gfxTextContextPaint::sUserDataKey
);
731 if (static_cast<nsSVGPathGeometryElement
*>(mContent
)->IsMarkable()) {
732 MarkerProperties properties
= GetMarkerProperties(this);
734 if (properties
.MarkersExist()) {
735 float strokeWidth
= nsSVGUtils::GetStrokeWidth(this, contextPaint
);
737 nsTArray
<nsSVGMark
> marks
;
738 static_cast<nsSVGPathGeometryElement
*>
739 (mContent
)->GetMarkPoints(&marks
);
741 uint32_t num
= marks
.Length();
743 // These are in the same order as the nsSVGMark::Type constants.
744 nsSVGMarkerFrame
* markerFrames
[] = {
745 properties
.GetMarkerStartFrame(),
746 properties
.GetMarkerMidFrame(),
747 properties
.GetMarkerEndFrame(),
749 PR_STATIC_ASSERT(MOZ_ARRAY_LENGTH(markerFrames
) == nsSVGMark::eTypeCount
);
751 for (uint32_t i
= 0; i
< num
; i
++) {
752 nsSVGMark
& mark
= marks
[i
];
753 nsSVGMarkerFrame
* frame
= markerFrames
[mark
.type
];
755 frame
->PaintMark(aContext
, this, &mark
, strokeWidth
);
764 nsSVGPathGeometryFrame::GetHitTestFlags()
766 return nsSVGUtils::GetGeometryHitTestFlags(this);