1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/svg/nsSVGPathGeometryFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,731 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// Main header first: 1.10 +#include "nsSVGPathGeometryFrame.h" 1.11 + 1.12 +// Keep others in (case-insensitive) order: 1.13 +#include "gfxContext.h" 1.14 +#include "gfxPlatform.h" 1.15 +#include "gfxSVGGlyphs.h" 1.16 +#include "nsDisplayList.h" 1.17 +#include "nsGkAtoms.h" 1.18 +#include "nsRenderingContext.h" 1.19 +#include "nsSVGEffects.h" 1.20 +#include "nsSVGIntegrationUtils.h" 1.21 +#include "nsSVGMarkerFrame.h" 1.22 +#include "nsSVGPathGeometryElement.h" 1.23 +#include "nsSVGUtils.h" 1.24 +#include "mozilla/ArrayUtils.h" 1.25 +#include "SVGAnimatedTransformList.h" 1.26 +#include "SVGGraphicsElement.h" 1.27 + 1.28 +using namespace mozilla; 1.29 +using namespace mozilla::gfx; 1.30 + 1.31 +//---------------------------------------------------------------------- 1.32 +// Implementation 1.33 + 1.34 +nsIFrame* 1.35 +NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell, 1.36 + nsStyleContext* aContext) 1.37 +{ 1.38 + return new (aPresShell) nsSVGPathGeometryFrame(aContext); 1.39 +} 1.40 + 1.41 +NS_IMPL_FRAMEARENA_HELPERS(nsSVGPathGeometryFrame) 1.42 + 1.43 +//---------------------------------------------------------------------- 1.44 +// nsQueryFrame methods 1.45 + 1.46 +NS_QUERYFRAME_HEAD(nsSVGPathGeometryFrame) 1.47 + NS_QUERYFRAME_ENTRY(nsISVGChildFrame) 1.48 + NS_QUERYFRAME_ENTRY(nsSVGPathGeometryFrame) 1.49 +NS_QUERYFRAME_TAIL_INHERITING(nsSVGPathGeometryFrameBase) 1.50 + 1.51 +//---------------------------------------------------------------------- 1.52 +// Display list item: 1.53 + 1.54 +class nsDisplaySVGPathGeometry : public nsDisplayItem { 1.55 +public: 1.56 + nsDisplaySVGPathGeometry(nsDisplayListBuilder* aBuilder, 1.57 + nsSVGPathGeometryFrame* aFrame) 1.58 + : nsDisplayItem(aBuilder, aFrame) 1.59 + { 1.60 + MOZ_COUNT_CTOR(nsDisplaySVGPathGeometry); 1.61 + NS_ABORT_IF_FALSE(aFrame, "Must have a frame!"); 1.62 + } 1.63 +#ifdef NS_BUILD_REFCNT_LOGGING 1.64 + virtual ~nsDisplaySVGPathGeometry() { 1.65 + MOZ_COUNT_DTOR(nsDisplaySVGPathGeometry); 1.66 + } 1.67 +#endif 1.68 + 1.69 + NS_DISPLAY_DECL_NAME("nsDisplaySVGPathGeometry", TYPE_SVG_PATH_GEOMETRY) 1.70 + 1.71 + virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, 1.72 + HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames); 1.73 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.74 + nsRenderingContext* aCtx); 1.75 +}; 1.76 + 1.77 +void 1.78 +nsDisplaySVGPathGeometry::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, 1.79 + HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) 1.80 +{ 1.81 + nsSVGPathGeometryFrame *frame = static_cast<nsSVGPathGeometryFrame*>(mFrame); 1.82 + nsPoint pointRelativeToReferenceFrame = aRect.Center(); 1.83 + // ToReferenceFrame() includes frame->GetPosition(), our user space position. 1.84 + nsPoint userSpacePt = pointRelativeToReferenceFrame - 1.85 + (ToReferenceFrame() - frame->GetPosition()); 1.86 + if (frame->GetFrameForPoint(userSpacePt)) { 1.87 + aOutFrames->AppendElement(frame); 1.88 + } 1.89 +} 1.90 + 1.91 +void 1.92 +nsDisplaySVGPathGeometry::Paint(nsDisplayListBuilder* aBuilder, 1.93 + nsRenderingContext* aCtx) 1.94 +{ 1.95 + // ToReferenceFrame includes our mRect offset, but painting takes 1.96 + // account of that too. To avoid double counting, we subtract that 1.97 + // here. 1.98 + nsPoint offset = ToReferenceFrame() - mFrame->GetPosition(); 1.99 + 1.100 + aCtx->PushState(); 1.101 + aCtx->Translate(offset); 1.102 + static_cast<nsSVGPathGeometryFrame*>(mFrame)->PaintSVG(aCtx, nullptr); 1.103 + aCtx->PopState(); 1.104 +} 1.105 + 1.106 +//---------------------------------------------------------------------- 1.107 +// nsIFrame methods 1.108 + 1.109 +void 1.110 +nsSVGPathGeometryFrame::Init(nsIContent* aContent, 1.111 + nsIFrame* aParent, 1.112 + nsIFrame* aPrevInFlow) 1.113 +{ 1.114 + AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD); 1.115 + nsSVGPathGeometryFrameBase::Init(aContent, aParent, aPrevInFlow); 1.116 +} 1.117 + 1.118 +nsresult 1.119 +nsSVGPathGeometryFrame::AttributeChanged(int32_t aNameSpaceID, 1.120 + nsIAtom* aAttribute, 1.121 + int32_t aModType) 1.122 +{ 1.123 + // We don't invalidate for transform changes (the layers code does that). 1.124 + // Also note that SVGTransformableElement::GetAttributeChangeHint will 1.125 + // return nsChangeHint_UpdateOverflow for "transform" attribute changes 1.126 + // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call. 1.127 + 1.128 + if (aNameSpaceID == kNameSpaceID_None && 1.129 + (static_cast<nsSVGPathGeometryElement*> 1.130 + (mContent)->AttributeDefinesGeometry(aAttribute))) { 1.131 + nsSVGEffects::InvalidateRenderingObservers(this); 1.132 + nsSVGUtils::ScheduleReflowSVG(this); 1.133 + } 1.134 + return NS_OK; 1.135 +} 1.136 + 1.137 +/* virtual */ void 1.138 +nsSVGPathGeometryFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) 1.139 +{ 1.140 + nsSVGPathGeometryFrameBase::DidSetStyleContext(aOldStyleContext); 1.141 + 1.142 + if (aOldStyleContext) { 1.143 + float oldOpacity = aOldStyleContext->PeekStyleDisplay()->mOpacity; 1.144 + float newOpacity = StyleDisplay()->mOpacity; 1.145 + if (newOpacity != oldOpacity && 1.146 + nsSVGUtils::CanOptimizeOpacity(this)) { 1.147 + // nsIFrame::BuildDisplayListForStackingContext() is not going to create an 1.148 + // nsDisplayOpacity display list item, so DLBI won't invalidate for us. 1.149 + InvalidateFrame(); 1.150 + } 1.151 + } 1.152 +} 1.153 + 1.154 +nsIAtom * 1.155 +nsSVGPathGeometryFrame::GetType() const 1.156 +{ 1.157 + return nsGkAtoms::svgPathGeometryFrame; 1.158 +} 1.159 + 1.160 +bool 1.161 +nsSVGPathGeometryFrame::IsSVGTransformed(gfx::Matrix *aOwnTransform, 1.162 + gfx::Matrix *aFromParentTransform) const 1.163 +{ 1.164 + bool foundTransform = false; 1.165 + 1.166 + // Check if our parent has children-only transforms: 1.167 + nsIFrame *parent = GetParent(); 1.168 + if (parent && 1.169 + parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) { 1.170 + foundTransform = static_cast<nsSVGContainerFrame*>(parent)-> 1.171 + HasChildrenOnlyTransform(aFromParentTransform); 1.172 + } 1.173 + 1.174 + nsSVGElement *content = static_cast<nsSVGElement*>(mContent); 1.175 + nsSVGAnimatedTransformList* transformList = 1.176 + content->GetAnimatedTransformList(); 1.177 + if ((transformList && transformList->HasTransform()) || 1.178 + content->GetAnimateMotionTransform()) { 1.179 + if (aOwnTransform) { 1.180 + *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(gfxMatrix(), 1.181 + nsSVGElement::eUserSpaceToParent)); 1.182 + } 1.183 + foundTransform = true; 1.184 + } 1.185 + return foundTransform; 1.186 +} 1.187 + 1.188 +void 1.189 +nsSVGPathGeometryFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.190 + const nsRect& aDirtyRect, 1.191 + const nsDisplayListSet& aLists) 1.192 +{ 1.193 + if (!static_cast<const nsSVGElement*>(mContent)->HasValidDimensions()) { 1.194 + return; 1.195 + } 1.196 + aLists.Content()->AppendNewToTop( 1.197 + new (aBuilder) nsDisplaySVGPathGeometry(aBuilder, this)); 1.198 +} 1.199 + 1.200 +//---------------------------------------------------------------------- 1.201 +// nsISVGChildFrame methods 1.202 + 1.203 +nsresult 1.204 +nsSVGPathGeometryFrame::PaintSVG(nsRenderingContext *aContext, 1.205 + const nsIntRect *aDirtyRect, 1.206 + nsIFrame* aTransformRoot) 1.207 +{ 1.208 + if (!StyleVisibility()->IsVisible()) 1.209 + return NS_OK; 1.210 + 1.211 + uint32_t paintOrder = StyleSVG()->mPaintOrder; 1.212 + if (paintOrder == NS_STYLE_PAINT_ORDER_NORMAL) { 1.213 + Render(aContext, eRenderFill | eRenderStroke, aTransformRoot); 1.214 + PaintMarkers(aContext); 1.215 + } else { 1.216 + while (paintOrder) { 1.217 + uint32_t component = 1.218 + paintOrder & ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1); 1.219 + switch (component) { 1.220 + case NS_STYLE_PAINT_ORDER_FILL: 1.221 + Render(aContext, eRenderFill, aTransformRoot); 1.222 + break; 1.223 + case NS_STYLE_PAINT_ORDER_STROKE: 1.224 + Render(aContext, eRenderStroke, aTransformRoot); 1.225 + break; 1.226 + case NS_STYLE_PAINT_ORDER_MARKERS: 1.227 + PaintMarkers(aContext); 1.228 + break; 1.229 + } 1.230 + paintOrder >>= NS_STYLE_PAINT_ORDER_BITWIDTH; 1.231 + } 1.232 + } 1.233 + 1.234 + return NS_OK; 1.235 +} 1.236 + 1.237 +nsIFrame* 1.238 +nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint) 1.239 +{ 1.240 + gfxMatrix canvasTM = GetCanvasTM(FOR_HIT_TESTING); 1.241 + if (canvasTM.IsSingular()) { 1.242 + return nullptr; 1.243 + } 1.244 + uint16_t fillRule, hitTestFlags; 1.245 + if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) { 1.246 + hitTestFlags = SVG_HIT_TEST_FILL; 1.247 + fillRule = StyleSVG()->mClipRule; 1.248 + } else { 1.249 + hitTestFlags = GetHitTestFlags(); 1.250 + // XXX once bug 614732 is fixed, aPoint won't need any conversion in order 1.251 + // to compare it with mRect. 1.252 + nsPoint point = 1.253 + nsSVGUtils::TransformOuterSVGPointToChildFrame(aPoint, canvasTM, PresContext()); 1.254 + if (!hitTestFlags || ((hitTestFlags & SVG_HIT_TEST_CHECK_MRECT) && 1.255 + !mRect.Contains(point))) 1.256 + return nullptr; 1.257 + fillRule = StyleSVG()->mFillRule; 1.258 + } 1.259 + 1.260 + bool isHit = false; 1.261 + 1.262 + nsRefPtr<gfxContext> tmpCtx = 1.263 + new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface()); 1.264 + 1.265 + GeneratePath(tmpCtx, ToMatrix(canvasTM)); 1.266 + gfxPoint userSpacePoint = 1.267 + tmpCtx->DeviceToUser(gfxPoint(PresContext()->AppUnitsToGfxUnits(aPoint.x), 1.268 + PresContext()->AppUnitsToGfxUnits(aPoint.y))); 1.269 + 1.270 + if (fillRule == NS_STYLE_FILL_RULE_EVENODD) 1.271 + tmpCtx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD); 1.272 + else 1.273 + tmpCtx->SetFillRule(gfxContext::FILL_RULE_WINDING); 1.274 + 1.275 + if (hitTestFlags & SVG_HIT_TEST_FILL) 1.276 + isHit = tmpCtx->PointInFill(userSpacePoint); 1.277 + if (!isHit && (hitTestFlags & SVG_HIT_TEST_STROKE)) { 1.278 + nsSVGUtils::SetupCairoStrokeGeometry(this, tmpCtx); 1.279 + // tmpCtx's matrix may have transformed by SetupCairoStrokeGeometry 1.280 + // if there is a non-scaling stroke. We need to transform userSpacePoint 1.281 + // so that everything is using the same co-ordinate system. 1.282 + userSpacePoint = 1.283 + nsSVGUtils::GetStrokeTransform(this).Invert().Transform(userSpacePoint); 1.284 + isHit = tmpCtx->PointInStroke(userSpacePoint); 1.285 + } 1.286 + 1.287 + if (isHit && nsSVGUtils::HitTestClip(this, aPoint)) 1.288 + return this; 1.289 + 1.290 + return nullptr; 1.291 +} 1.292 + 1.293 +nsRect 1.294 +nsSVGPathGeometryFrame::GetCoveredRegion() 1.295 +{ 1.296 + return nsSVGUtils::TransformFrameRectToOuterSVG( 1.297 + mRect, GetCanvasTM(FOR_OUTERSVG_TM), PresContext()); 1.298 +} 1.299 + 1.300 +void 1.301 +nsSVGPathGeometryFrame::ReflowSVG() 1.302 +{ 1.303 + NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this), 1.304 + "This call is probably a wasteful mistake"); 1.305 + 1.306 + NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY), 1.307 + "ReflowSVG mechanism not designed for this"); 1.308 + 1.309 + if (!nsSVGUtils::NeedsReflowSVG(this)) { 1.310 + return; 1.311 + } 1.312 + 1.313 + uint32_t flags = nsSVGUtils::eBBoxIncludeFill | 1.314 + nsSVGUtils::eBBoxIncludeStroke | 1.315 + nsSVGUtils::eBBoxIncludeMarkers; 1.316 + // Our "visual" overflow rect needs to be valid for building display lists 1.317 + // for hit testing, which means that for certain values of 'pointer-events' 1.318 + // it needs to include the geometry of the fill or stroke even when the fill/ 1.319 + // stroke don't actually render (e.g. when stroke="none" or 1.320 + // stroke-opacity="0"). GetHitTestFlags() accounts for 'pointer-events'. 1.321 + uint16_t hitTestFlags = GetHitTestFlags(); 1.322 + if ((hitTestFlags & SVG_HIT_TEST_FILL)) { 1.323 + flags |= nsSVGUtils::eBBoxIncludeFillGeometry; 1.324 + } 1.325 + if ((hitTestFlags & SVG_HIT_TEST_STROKE)) { 1.326 + flags |= nsSVGUtils::eBBoxIncludeStrokeGeometry; 1.327 + } 1.328 + 1.329 + // We'd like to just pass the identity matrix to GetBBoxContribution, but if 1.330 + // this frame's user space size is _very_ large/small then the extents we 1.331 + // obtain below might have overflowed or otherwise be broken. This would 1.332 + // cause us to end up with a broken mRect and visual overflow rect and break 1.333 + // painting of this frame. This is particularly noticeable if the transforms 1.334 + // between us and our nsSVGOuterSVGFrame scale this frame to a reasonable 1.335 + // size. To avoid this we sadly have to do extra work to account for the 1.336 + // transforms between us and our nsSVGOuterSVGFrame, even though the 1.337 + // overwhelming number of SVGs will never have this problem. 1.338 + // XXX Will Azure eventually save us from having to do this? 1.339 + gfxSize scaleFactors = GetCanvasTM(FOR_OUTERSVG_TM).ScaleFactors(true); 1.340 + bool applyScaling = fabs(scaleFactors.width) >= 1e-6 && 1.341 + fabs(scaleFactors.height) >= 1e-6; 1.342 + gfx::Matrix scaling; 1.343 + if (applyScaling) { 1.344 + scaling.Scale(scaleFactors.width, scaleFactors.height); 1.345 + } 1.346 + gfxRect extent = GetBBoxContribution(scaling, flags).ToThebesRect(); 1.347 + if (applyScaling) { 1.348 + extent.Scale(1 / scaleFactors.width, 1 / scaleFactors.height); 1.349 + } 1.350 + mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, 1.351 + PresContext()->AppUnitsPerCSSPixel()); 1.352 + 1.353 + if (mState & NS_FRAME_FIRST_REFLOW) { 1.354 + // Make sure we have our filter property (if any) before calling 1.355 + // FinishAndStoreOverflow (subsequent filter changes are handled off 1.356 + // nsChangeHint_UpdateEffects): 1.357 + nsSVGEffects::UpdateEffects(this); 1.358 + } 1.359 + 1.360 + nsRect overflow = nsRect(nsPoint(0,0), mRect.Size()); 1.361 + nsOverflowAreas overflowAreas(overflow, overflow); 1.362 + FinishAndStoreOverflow(overflowAreas, mRect.Size()); 1.363 + 1.364 + mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | 1.365 + NS_FRAME_HAS_DIRTY_CHILDREN); 1.366 + 1.367 + // Invalidate, but only if this is not our first reflow (since if it is our 1.368 + // first reflow then we haven't had our first paint yet). 1.369 + if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { 1.370 + InvalidateFrame(); 1.371 + } 1.372 +} 1.373 + 1.374 +void 1.375 +nsSVGPathGeometryFrame::NotifySVGChanged(uint32_t aFlags) 1.376 +{ 1.377 + NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED), 1.378 + "Invalidation logic may need adjusting"); 1.379 + 1.380 + // Changes to our ancestors may affect how we render when we are rendered as 1.381 + // part of our ancestor (specifically, if our coordinate context changes size 1.382 + // and we have percentage lengths defining our geometry, then we need to be 1.383 + // reflowed). However, ancestor changes cannot affect how we render when we 1.384 + // are rendered as part of any rendering observers that we may have. 1.385 + // Therefore no need to notify rendering observers here. 1.386 + 1.387 + // Don't try to be too smart trying to avoid the ScheduleReflowSVG calls 1.388 + // for the stroke properties examined below. Checking HasStroke() is not 1.389 + // enough, since what we care about is whether we include the stroke in our 1.390 + // overflow rects or not, and we sometimes deliberately include stroke 1.391 + // when it's not visible. See the complexities of GetBBoxContribution. 1.392 + 1.393 + if (aFlags & COORD_CONTEXT_CHANGED) { 1.394 + // Stroke currently contributes to our mRect, which is why we have to take 1.395 + // account of stroke-width here. Note that we do not need to take account 1.396 + // of stroke-dashoffset since, although that can have a percentage value 1.397 + // that is resolved against our coordinate context, it does not affect our 1.398 + // mRect. 1.399 + if (static_cast<nsSVGPathGeometryElement*>(mContent)->GeometryDependsOnCoordCtx() || 1.400 + StyleSVG()->mStrokeWidth.HasPercent()) { 1.401 + nsSVGUtils::ScheduleReflowSVG(this); 1.402 + } 1.403 + } 1.404 + 1.405 + if ((aFlags & TRANSFORM_CHANGED) && 1.406 + StyleSVGReset()->mVectorEffect == 1.407 + NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) { 1.408 + // Stroke currently contributes to our mRect, and our stroke depends on 1.409 + // the transform to our outer-<svg> if |vector-effect:non-scaling-stroke|. 1.410 + nsSVGUtils::ScheduleReflowSVG(this); 1.411 + } 1.412 +} 1.413 + 1.414 +SVGBBox 1.415 +nsSVGPathGeometryFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace, 1.416 + uint32_t aFlags) 1.417 +{ 1.418 + SVGBBox bbox; 1.419 + 1.420 + if (aToBBoxUserspace.IsSingular()) { 1.421 + // XXX ReportToConsole 1.422 + return bbox; 1.423 + } 1.424 + 1.425 + nsRefPtr<gfxContext> tmpCtx = 1.426 + new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface()); 1.427 + 1.428 + GeneratePath(tmpCtx, aToBBoxUserspace); 1.429 + tmpCtx->IdentityMatrix(); 1.430 + 1.431 + // Be careful when replacing the following logic to get the fill and stroke 1.432 + // extents independently (instead of computing the stroke extents from the 1.433 + // path extents). You may think that you can just use the stroke extents if 1.434 + // there is both a fill and a stroke. In reality it's necessary to calculate 1.435 + // both the fill and stroke extents, and take the union of the two. There are 1.436 + // two reasons for this: 1.437 + // 1.438 + // # Due to stroke dashing, in certain cases the fill extents could actually 1.439 + // extend outside the stroke extents. 1.440 + // # If the stroke is very thin, cairo won't paint any stroke, and so the 1.441 + // stroke bounds that it will return will be empty. 1.442 + 1.443 + gfxRect pathExtents = tmpCtx->GetUserPathExtent(); 1.444 + 1.445 + // Account for fill: 1.446 + if ((aFlags & nsSVGUtils::eBBoxIncludeFillGeometry) || 1.447 + ((aFlags & nsSVGUtils::eBBoxIncludeFill) && 1.448 + StyleSVG()->mFill.mType != eStyleSVGPaintType_None)) { 1.449 + bbox = pathExtents; 1.450 + } 1.451 + 1.452 + // Account for stroke: 1.453 + if ((aFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) || 1.454 + ((aFlags & nsSVGUtils::eBBoxIncludeStroke) && 1.455 + nsSVGUtils::HasStroke(this))) { 1.456 + // We can't use tmpCtx->GetUserStrokeExtent() since it doesn't work for 1.457 + // device space extents. Instead we approximate the stroke extents from 1.458 + // pathExtents using PathExtentsToMaxStrokeExtents. 1.459 + if (pathExtents.Width() <= 0 && pathExtents.Height() <= 0) { 1.460 + // We have a zero length path, but it may still have non-empty stroke 1.461 + // bounds depending on the value of stroke-linecap. We need to fix up 1.462 + // pathExtents before it can be used with PathExtentsToMaxStrokeExtents 1.463 + // though, because if pathExtents is empty, its position will not have 1.464 + // been set. Happily we can use tmpCtx->GetUserStrokeExtent() to find 1.465 + // the center point of the extents even though it gets the extents wrong. 1.466 + nsSVGUtils::SetupCairoStrokeBBoxGeometry(this, tmpCtx); 1.467 + pathExtents.MoveTo(tmpCtx->GetUserStrokeExtent().Center()); 1.468 + pathExtents.SizeTo(0, 0); 1.469 + } 1.470 + bbox.UnionEdges(nsSVGUtils::PathExtentsToMaxStrokeExtents(pathExtents, 1.471 + this, 1.472 + ThebesMatrix(aToBBoxUserspace))); 1.473 + } 1.474 + 1.475 + // Account for markers: 1.476 + if ((aFlags & nsSVGUtils::eBBoxIncludeMarkers) != 0 && 1.477 + static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) { 1.478 + 1.479 + float strokeWidth = nsSVGUtils::GetStrokeWidth(this); 1.480 + MarkerProperties properties = GetMarkerProperties(this); 1.481 + 1.482 + if (properties.MarkersExist()) { 1.483 + nsTArray<nsSVGMark> marks; 1.484 + static_cast<nsSVGPathGeometryElement*>(mContent)->GetMarkPoints(&marks); 1.485 + uint32_t num = marks.Length(); 1.486 + 1.487 + // These are in the same order as the nsSVGMark::Type constants. 1.488 + nsSVGMarkerFrame* markerFrames[] = { 1.489 + properties.GetMarkerStartFrame(), 1.490 + properties.GetMarkerMidFrame(), 1.491 + properties.GetMarkerEndFrame(), 1.492 + }; 1.493 + PR_STATIC_ASSERT(MOZ_ARRAY_LENGTH(markerFrames) == nsSVGMark::eTypeCount); 1.494 + 1.495 + for (uint32_t i = 0; i < num; i++) { 1.496 + nsSVGMark& mark = marks[i]; 1.497 + nsSVGMarkerFrame* frame = markerFrames[mark.type]; 1.498 + if (frame) { 1.499 + SVGBBox mbbox = 1.500 + frame->GetMarkBBoxContribution(aToBBoxUserspace, aFlags, this, 1.501 + &marks[i], strokeWidth); 1.502 + bbox.UnionEdges(mbbox); 1.503 + } 1.504 + } 1.505 + } 1.506 + } 1.507 + 1.508 + return bbox; 1.509 +} 1.510 + 1.511 +//---------------------------------------------------------------------- 1.512 +// nsSVGPathGeometryFrame methods: 1.513 + 1.514 +gfxMatrix 1.515 +nsSVGPathGeometryFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot) 1.516 +{ 1.517 + if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) && !aTransformRoot) { 1.518 + if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) || 1.519 + (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) { 1.520 + return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this); 1.521 + } 1.522 + } 1.523 + 1.524 + NS_ASSERTION(mParent, "null parent"); 1.525 + 1.526 + nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent); 1.527 + dom::SVGGraphicsElement *content = static_cast<dom::SVGGraphicsElement*>(mContent); 1.528 + 1.529 + return content->PrependLocalTransformsTo( 1.530 + this == aTransformRoot ? gfxMatrix() : 1.531 + parent->GetCanvasTM(aFor, aTransformRoot)); 1.532 +} 1.533 + 1.534 +nsSVGPathGeometryFrame::MarkerProperties 1.535 +nsSVGPathGeometryFrame::GetMarkerProperties(nsSVGPathGeometryFrame *aFrame) 1.536 +{ 1.537 + NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation"); 1.538 + 1.539 + MarkerProperties result; 1.540 + const nsStyleSVG *style = aFrame->StyleSVG(); 1.541 + result.mMarkerStart = 1.542 + nsSVGEffects::GetMarkerProperty(style->mMarkerStart, aFrame, 1.543 + nsSVGEffects::MarkerBeginProperty()); 1.544 + result.mMarkerMid = 1.545 + nsSVGEffects::GetMarkerProperty(style->mMarkerMid, aFrame, 1.546 + nsSVGEffects::MarkerMiddleProperty()); 1.547 + result.mMarkerEnd = 1.548 + nsSVGEffects::GetMarkerProperty(style->mMarkerEnd, aFrame, 1.549 + nsSVGEffects::MarkerEndProperty()); 1.550 + return result; 1.551 +} 1.552 + 1.553 +nsSVGMarkerFrame * 1.554 +nsSVGPathGeometryFrame::MarkerProperties::GetMarkerStartFrame() 1.555 +{ 1.556 + if (!mMarkerStart) 1.557 + return nullptr; 1.558 + return static_cast<nsSVGMarkerFrame *> 1.559 + (mMarkerStart->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr)); 1.560 +} 1.561 + 1.562 +nsSVGMarkerFrame * 1.563 +nsSVGPathGeometryFrame::MarkerProperties::GetMarkerMidFrame() 1.564 +{ 1.565 + if (!mMarkerMid) 1.566 + return nullptr; 1.567 + return static_cast<nsSVGMarkerFrame *> 1.568 + (mMarkerMid->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr)); 1.569 +} 1.570 + 1.571 +nsSVGMarkerFrame * 1.572 +nsSVGPathGeometryFrame::MarkerProperties::GetMarkerEndFrame() 1.573 +{ 1.574 + if (!mMarkerEnd) 1.575 + return nullptr; 1.576 + return static_cast<nsSVGMarkerFrame *> 1.577 + (mMarkerEnd->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr)); 1.578 +} 1.579 + 1.580 +void 1.581 +nsSVGPathGeometryFrame::Render(nsRenderingContext *aContext, 1.582 + uint32_t aRenderComponents, 1.583 + nsIFrame* aTransformRoot) 1.584 +{ 1.585 + gfxContext *gfx = aContext->ThebesContext(); 1.586 + 1.587 + uint16_t renderMode = SVGAutoRenderState::GetRenderMode(aContext); 1.588 + 1.589 + switch (StyleSVG()->mShapeRendering) { 1.590 + case NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED: 1.591 + case NS_STYLE_SHAPE_RENDERING_CRISPEDGES: 1.592 + gfx->SetAntialiasMode(gfxContext::MODE_ALIASED); 1.593 + break; 1.594 + default: 1.595 + gfx->SetAntialiasMode(gfxContext::MODE_COVERAGE); 1.596 + break; 1.597 + } 1.598 + 1.599 + if (renderMode != SVGAutoRenderState::NORMAL) { 1.600 + NS_ABORT_IF_FALSE(renderMode == SVGAutoRenderState::CLIP || 1.601 + renderMode == SVGAutoRenderState::CLIP_MASK, 1.602 + "Unknown render mode"); 1.603 + 1.604 + // In the case that |renderMode == SVGAutoRenderState::CLIP| then we don't 1.605 + // use the path we generate here until further up the call stack when 1.606 + // nsSVGClipPathFrame::Clip calls gfxContext::Clip. That's a problem for 1.607 + // Moz2D which emits paths in user space (unlike cairo which emits paths in 1.608 + // device space). gfxContext has hacks to deal with code changing the 1.609 + // transform then using the current path when it is backed by Moz2D, but 1.610 + // Moz2D itself does not since that would fundamentally go against its API. 1.611 + // Therefore we do not want to Save()/Restore() the gfxContext here in the 1.612 + // SVGAutoRenderState::CLIP case since that would block us from killing off 1.613 + // gfxContext and using Moz2D directly. Not bothering to Save()/Restore() 1.614 + // is actually okay, since we know that doesn't matter in the 1.615 + // SVGAutoRenderState::CLIP case (at least for the current implementation). 1.616 + gfxContextMatrixAutoSaveRestore autoSaveRestore; 1.617 + // For now revent back to doing the save even for CLIP to fix bug 959128. 1.618 + // Undo in bug 987193. 1.619 + //if (renderMode != SVGAutoRenderState::CLIP) { 1.620 + autoSaveRestore.SetContext(gfx); 1.621 + //} 1.622 + 1.623 + GeneratePath(gfx, ToMatrix(GetCanvasTM(FOR_PAINTING, aTransformRoot))); 1.624 + 1.625 + // We used to call gfx->Restore() here, since for the 1.626 + // SVGAutoRenderState::CLIP case it is important to leave the fill rule 1.627 + // that we set below untouched so that the value is still set when return 1.628 + // to gfxContext::Clip() further up the call stack. Since we no longer 1.629 + // call gfx->Save() in the SVGAutoRenderState::CLIP case we don't need to 1.630 + // worry that autoSaveRestore will delay the Restore() call for the 1.631 + // CLIP_MASK case until we exit this function. 1.632 + 1.633 + gfxContext::FillRule oldFillRull = gfx->CurrentFillRule(); 1.634 + 1.635 + if (StyleSVG()->mClipRule == NS_STYLE_FILL_RULE_EVENODD) 1.636 + gfx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD); 1.637 + else 1.638 + gfx->SetFillRule(gfxContext::FILL_RULE_WINDING); 1.639 + 1.640 + if (renderMode == SVGAutoRenderState::CLIP_MASK) { 1.641 + gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f)); 1.642 + gfx->Fill(); 1.643 + gfx->SetFillRule(oldFillRull); // restore, but only for CLIP_MASK 1.644 + gfx->NewPath(); 1.645 + } 1.646 + 1.647 + return; 1.648 + } 1.649 + 1.650 + gfxContextAutoSaveRestore autoSaveRestore(gfx); 1.651 + 1.652 + GeneratePath(gfx, ToMatrix(GetCanvasTM(FOR_PAINTING, aTransformRoot))); 1.653 + 1.654 + gfxTextContextPaint *contextPaint = 1.655 + (gfxTextContextPaint*)aContext->GetUserData(&gfxTextContextPaint::sUserDataKey); 1.656 + 1.657 + if ((aRenderComponents & eRenderFill) && 1.658 + nsSVGUtils::SetupCairoFillPaint(this, gfx, contextPaint)) { 1.659 + gfx->Fill(); 1.660 + } 1.661 + 1.662 + if ((aRenderComponents & eRenderStroke) && 1.663 + nsSVGUtils::SetupCairoStroke(this, gfx, contextPaint)) { 1.664 + gfx->Stroke(); 1.665 + } 1.666 + 1.667 + gfx->NewPath(); 1.668 +} 1.669 + 1.670 +void 1.671 +nsSVGPathGeometryFrame::GeneratePath(gfxContext* aContext, 1.672 + const Matrix &aTransform) 1.673 +{ 1.674 + if (aTransform.IsSingular()) { 1.675 + aContext->IdentityMatrix(); 1.676 + aContext->NewPath(); 1.677 + return; 1.678 + } 1.679 + 1.680 + aContext->MultiplyAndNudgeToIntegers(ThebesMatrix(aTransform)); 1.681 + 1.682 + // Hack to let SVGPathData::ConstructPath know if we have square caps: 1.683 + const nsStyleSVG* style = StyleSVG(); 1.684 + if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) { 1.685 + aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE); 1.686 + } 1.687 + 1.688 + aContext->NewPath(); 1.689 + static_cast<nsSVGPathGeometryElement*>(mContent)->ConstructPath(aContext); 1.690 +} 1.691 + 1.692 +void 1.693 +nsSVGPathGeometryFrame::PaintMarkers(nsRenderingContext* aContext) 1.694 +{ 1.695 + gfxTextContextPaint *contextPaint = 1.696 + (gfxTextContextPaint*)aContext->GetUserData(&gfxTextContextPaint::sUserDataKey); 1.697 + 1.698 + if (static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) { 1.699 + MarkerProperties properties = GetMarkerProperties(this); 1.700 + 1.701 + if (properties.MarkersExist()) { 1.702 + float strokeWidth = nsSVGUtils::GetStrokeWidth(this, contextPaint); 1.703 + 1.704 + nsTArray<nsSVGMark> marks; 1.705 + static_cast<nsSVGPathGeometryElement*> 1.706 + (mContent)->GetMarkPoints(&marks); 1.707 + 1.708 + uint32_t num = marks.Length(); 1.709 + if (num) { 1.710 + // These are in the same order as the nsSVGMark::Type constants. 1.711 + nsSVGMarkerFrame* markerFrames[] = { 1.712 + properties.GetMarkerStartFrame(), 1.713 + properties.GetMarkerMidFrame(), 1.714 + properties.GetMarkerEndFrame(), 1.715 + }; 1.716 + PR_STATIC_ASSERT(MOZ_ARRAY_LENGTH(markerFrames) == nsSVGMark::eTypeCount); 1.717 + 1.718 + for (uint32_t i = 0; i < num; i++) { 1.719 + nsSVGMark& mark = marks[i]; 1.720 + nsSVGMarkerFrame* frame = markerFrames[mark.type]; 1.721 + if (frame) { 1.722 + frame->PaintMark(aContext, this, &mark, strokeWidth); 1.723 + } 1.724 + } 1.725 + } 1.726 + } 1.727 + } 1.728 +} 1.729 + 1.730 +uint16_t 1.731 +nsSVGPathGeometryFrame::GetHitTestFlags() 1.732 +{ 1.733 + return nsSVGUtils::GetGeometryHitTestFlags(this); 1.734 +}