Bumping manifests a=b2g-bump
[gecko.git] / layout / svg / nsSVGPathGeometryFrame.cpp
blobdaed1d4c7ecee7900488273b9b06c628fd89cad7
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/. */
6 // Main header first:
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 //----------------------------------------------------------------------
34 // Implementation
36 nsIFrame*
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 //----------------------------------------------------------------------
54 // Display list item:
56 class nsDisplaySVGPathGeometry : public nsDisplayItem {
57 public:
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);
69 #endif
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);
79 void
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);
96 void
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
102 // here.
103 nsPoint offset = ToReferenceFrame() - mFrame->GetPosition();
105 aCtx->PushState();
106 aCtx->Translate(offset);
107 static_cast<nsSVGPathGeometryFrame*>(mFrame)->PaintSVG(aCtx, nullptr);
108 aCtx->PopState();
111 //----------------------------------------------------------------------
112 // nsIFrame methods
114 void
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);
123 nsresult
124 nsSVGPathGeometryFrame::AttributeChanged(int32_t aNameSpaceID,
125 nsIAtom* aAttribute,
126 int32_t aModType)
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);
139 return NS_OK;
142 /* virtual */ void
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.
154 InvalidateFrame();
159 nsIAtom *
160 nsSVGPathGeometryFrame::GetType() const
162 return nsGkAtoms::svgPathGeometryFrame;
165 bool
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();
173 if (parent &&
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()) {
184 if (aOwnTransform) {
185 *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(gfxMatrix(),
186 nsSVGElement::eUserSpaceToParent));
188 foundTransform = true;
190 return foundTransform;
193 void
194 nsSVGPathGeometryFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
195 const nsRect& aDirtyRect,
196 const nsDisplayListSet& aLists)
198 if (!static_cast<const nsSVGElement*>(mContent)->HasValidDimensions()) {
199 return;
201 aLists.Content()->AppendNewToTop(
202 new (aBuilder) nsDisplaySVGPathGeometry(aBuilder, this));
205 //----------------------------------------------------------------------
206 // nsISVGChildFrame methods
208 nsresult
209 nsSVGPathGeometryFrame::PaintSVG(nsRenderingContext *aContext,
210 const nsIntRect *aDirtyRect,
211 nsIFrame* aTransformRoot)
213 if (!StyleVisibility()->IsVisible())
214 return NS_OK;
216 uint32_t paintOrder = StyleSVG()->mPaintOrder;
217 if (paintOrder == NS_STYLE_PAINT_ORDER_NORMAL) {
218 Render(aContext, eRenderFill | eRenderStroke, aTransformRoot);
219 PaintMarkers(aContext);
220 } else {
221 while (paintOrder) {
222 uint32_t component =
223 paintOrder & ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1);
224 switch (component) {
225 case NS_STYLE_PAINT_ORDER_FILL:
226 Render(aContext, eRenderFill, aTransformRoot);
227 break;
228 case NS_STYLE_PAINT_ORDER_STROKE:
229 Render(aContext, eRenderStroke, aTransformRoot);
230 break;
231 case NS_STYLE_PAINT_ORDER_MARKERS:
232 PaintMarkers(aContext);
233 break;
235 paintOrder >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
239 return NS_OK;
242 nsIFrame*
243 nsSVGPathGeometryFrame::GetFrameForPoint(const gfxPoint& aPoint)
245 FillRule fillRule;
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;
251 } else {
252 hitTestFlags = GetHitTestFlags();
253 if (!hitTestFlags) {
254 return nullptr;
256 if (hitTestFlags & SVG_HIT_TEST_CHECK_MRECT) {
257 gfxRect rect =
258 nsLayoutUtils::RectToGfxRect(mRect, PresContext()->AppUnitsPerCSSPixel());
259 if (!rect.Contains(aPoint)) {
260 return nullptr;
263 fillRule = StyleSVG()->mFillRule == NS_STYLE_FILL_RULE_NONZERO
264 ? FillRule::FILL_WINDING : FillRule::FILL_EVEN_ODD;
267 bool isHit = false;
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);
280 if (!path) {
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()) {
298 return nullptr;
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))
309 return this;
311 return nullptr;
314 nsRect
315 nsSVGPathGeometryFrame::GetCoveredRegion()
317 return nsSVGUtils::TransformFrameRectToOuterSVG(
318 mRect, GetCanvasTM(FOR_OUTERSVG_TM), PresContext());
321 void
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)) {
331 return;
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;
363 gfx::Matrix scaling;
364 if (applyScaling) {
365 scaling.Scale(scaleFactors.width, scaleFactors.height);
367 gfxRect extent = GetBBoxContribution(scaling, flags).ToThebesRect();
368 if (applyScaling) {
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)) {
391 InvalidateFrame();
395 void
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
419 // mRect.
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);
435 SVGBBox
436 nsSVGPathGeometryFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
437 uint32_t aFlags)
439 SVGBBox bbox;
441 if (aToBBoxUserspace.IsSingular()) {
442 // XXX ReportToConsole
443 return bbox;
446 RefPtr<DrawTarget> tmpDT;
447 #ifdef XP_WIN
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));
457 #else
458 tmpDT = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
459 #endif
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();
479 // Account for fill:
480 if ((aFlags & nsSVGUtils::eBBoxIncludeFillGeometry) ||
481 ((aFlags & nsSVGUtils::eBBoxIncludeFill) &&
482 StyleSVG()->mFill.mType != eStyleSVGPaintType_None)) {
483 bbox = pathExtents;
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,
505 this,
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];
532 if (frame) {
533 SVGBBox mbbox =
534 frame->GetMarkBBoxContribution(aToBBoxUserspace, aFlags, this,
535 &marks[i], strokeWidth);
536 bbox.UnionEdges(mbbox);
542 return bbox;
545 //----------------------------------------------------------------------
546 // nsSVGPathGeometryFrame methods:
548 gfxMatrix
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());
577 result.mMarkerMid =
578 nsSVGEffects::GetMarkerProperty(style->mMarkerMid, aFrame,
579 nsSVGEffects::MarkerMiddleProperty());
580 result.mMarkerEnd =
581 nsSVGEffects::GetMarkerProperty(style->mMarkerEnd, aFrame,
582 nsSVGEffects::MarkerEndProperty());
583 return result;
586 nsSVGMarkerFrame *
587 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerStartFrame()
589 if (!mMarkerStart)
590 return nullptr;
591 return static_cast<nsSVGMarkerFrame *>
592 (mMarkerStart->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
595 nsSVGMarkerFrame *
596 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerMidFrame()
598 if (!mMarkerMid)
599 return nullptr;
600 return static_cast<nsSVGMarkerFrame *>
601 (mMarkerMid->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
604 nsSVGMarkerFrame *
605 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerEndFrame()
607 if (!mMarkerEnd)
608 return nullptr;
609 return static_cast<nsSVGMarkerFrame *>
610 (mMarkerEnd->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
613 void
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);
626 break;
627 default:
628 gfx->SetAntialiasMode(gfxContext::MODE_COVERAGE);
629 break;
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);
670 else
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));
675 gfx->Fill();
676 gfx->SetFillRule(oldFillRull); // restore, but only for CLIP_MASK
677 gfx->NewPath();
680 return;
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)) {
692 gfx->Fill();
695 if ((aRenderComponents & eRenderStroke) &&
696 nsSVGUtils::SetupCairoStroke(this, gfx, contextPaint)) {
697 gfx->Stroke();
700 gfx->NewPath();
703 void
704 nsSVGPathGeometryFrame::GeneratePath(gfxContext* aContext,
705 const Matrix &aTransform)
707 if (aTransform.IsSingular()) {
708 aContext->IdentityMatrix();
709 aContext->NewPath();
710 return;
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);
721 aContext->NewPath();
722 static_cast<nsSVGPathGeometryElement*>(mContent)->ConstructPath(aContext);
725 void
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();
742 if (num) {
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];
754 if (frame) {
755 frame->PaintMark(aContext, this, &mark, strokeWidth);
763 uint16_t
764 nsSVGPathGeometryFrame::GetHitTestFlags()
766 return nsSVGUtils::GetGeometryHitTestFlags(this);