1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/svg/nsSVGMarkerFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,272 @@ 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 "nsSVGMarkerFrame.h" 1.11 + 1.12 +// Keep others in (case-insensitive) order: 1.13 +#include "gfxContext.h" 1.14 +#include "nsRenderingContext.h" 1.15 +#include "nsSVGEffects.h" 1.16 +#include "mozilla/dom/SVGMarkerElement.h" 1.17 +#include "nsSVGPathGeometryElement.h" 1.18 +#include "nsSVGPathGeometryFrame.h" 1.19 + 1.20 +using namespace mozilla::dom; 1.21 +using namespace mozilla::gfx; 1.22 + 1.23 +nsIFrame* 1.24 +NS_NewSVGMarkerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.25 +{ 1.26 + return new (aPresShell) nsSVGMarkerFrame(aContext); 1.27 +} 1.28 + 1.29 +NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerFrame) 1.30 + 1.31 +//---------------------------------------------------------------------- 1.32 +// nsIFrame methods: 1.33 + 1.34 +nsresult 1.35 +nsSVGMarkerFrame::AttributeChanged(int32_t aNameSpaceID, 1.36 + nsIAtom* aAttribute, 1.37 + int32_t aModType) 1.38 +{ 1.39 + if (aNameSpaceID == kNameSpaceID_None && 1.40 + (aAttribute == nsGkAtoms::markerUnits || 1.41 + aAttribute == nsGkAtoms::refX || 1.42 + aAttribute == nsGkAtoms::refY || 1.43 + aAttribute == nsGkAtoms::markerWidth || 1.44 + aAttribute == nsGkAtoms::markerHeight || 1.45 + aAttribute == nsGkAtoms::orient || 1.46 + aAttribute == nsGkAtoms::preserveAspectRatio || 1.47 + aAttribute == nsGkAtoms::viewBox)) { 1.48 + nsSVGEffects::InvalidateDirectRenderingObservers(this); 1.49 + } 1.50 + 1.51 + return nsSVGMarkerFrameBase::AttributeChanged(aNameSpaceID, 1.52 + aAttribute, aModType); 1.53 +} 1.54 + 1.55 +#ifdef DEBUG 1.56 +void 1.57 +nsSVGMarkerFrame::Init(nsIContent* aContent, 1.58 + nsIFrame* aParent, 1.59 + nsIFrame* aPrevInFlow) 1.60 +{ 1.61 + NS_ASSERTION(aContent->IsSVG(nsGkAtoms::marker), "Content is not an SVG marker"); 1.62 + 1.63 + nsSVGMarkerFrameBase::Init(aContent, aParent, aPrevInFlow); 1.64 +} 1.65 +#endif /* DEBUG */ 1.66 + 1.67 +nsIAtom * 1.68 +nsSVGMarkerFrame::GetType() const 1.69 +{ 1.70 + return nsGkAtoms::svgMarkerFrame; 1.71 +} 1.72 + 1.73 +//---------------------------------------------------------------------- 1.74 +// nsSVGContainerFrame methods: 1.75 + 1.76 +gfxMatrix 1.77 +nsSVGMarkerFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot) 1.78 +{ 1.79 + NS_ASSERTION(mMarkedFrame, "null nsSVGPathGeometry frame"); 1.80 + 1.81 + if (mInUse2) { 1.82 + // We're going to be bailing drawing the marker, so return an identity. 1.83 + return gfxMatrix(); 1.84 + } 1.85 + 1.86 + SVGMarkerElement *content = static_cast<SVGMarkerElement*>(mContent); 1.87 + 1.88 + mInUse2 = true; 1.89 + gfxMatrix markedTM = mMarkedFrame->GetCanvasTM(aFor, aTransformRoot); 1.90 + mInUse2 = false; 1.91 + 1.92 + Matrix markerTM = content->GetMarkerTransform(mStrokeWidth, mX, mY, 1.93 + mAutoAngle, mIsStart); 1.94 + Matrix viewBoxTM = content->GetViewBoxTransform(); 1.95 + 1.96 + return ThebesMatrix(viewBoxTM * markerTM) * markedTM; 1.97 +} 1.98 + 1.99 +static nsIFrame* 1.100 +GetAnonymousChildFrame(nsIFrame* aFrame) 1.101 +{ 1.102 + nsIFrame* kid = aFrame->GetFirstPrincipalChild(); 1.103 + MOZ_ASSERT(kid && kid->GetType() == nsGkAtoms::svgMarkerAnonChildFrame, 1.104 + "expected to find anonymous child of marker frame"); 1.105 + return kid; 1.106 +} 1.107 + 1.108 +nsresult 1.109 +nsSVGMarkerFrame::PaintMark(nsRenderingContext *aContext, 1.110 + nsSVGPathGeometryFrame *aMarkedFrame, 1.111 + nsSVGMark *aMark, float aStrokeWidth) 1.112 +{ 1.113 + // If the flag is set when we get here, it means this marker frame 1.114 + // has already been used painting the current mark, and the document 1.115 + // has a marker reference loop. 1.116 + if (mInUse) 1.117 + return NS_OK; 1.118 + 1.119 + AutoMarkerReferencer markerRef(this, aMarkedFrame); 1.120 + 1.121 + SVGMarkerElement *marker = static_cast<SVGMarkerElement*>(mContent); 1.122 + if (!marker->HasValidDimensions()) { 1.123 + return NS_OK; 1.124 + } 1.125 + 1.126 + const nsSVGViewBoxRect viewBox = marker->GetViewBoxRect(); 1.127 + 1.128 + if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) { 1.129 + // We must disable rendering if the viewBox width or height are zero. 1.130 + return NS_OK; 1.131 + } 1.132 + 1.133 + mStrokeWidth = aStrokeWidth; 1.134 + mX = aMark->x; 1.135 + mY = aMark->y; 1.136 + mAutoAngle = aMark->angle; 1.137 + mIsStart = aMark->type == nsSVGMark::eStart; 1.138 + 1.139 + gfxContext *gfx = aContext->ThebesContext(); 1.140 + 1.141 + if (StyleDisplay()->IsScrollableOverflow()) { 1.142 + gfx->Save(); 1.143 + gfxRect clipRect = 1.144 + nsSVGUtils::GetClipRectForFrame(this, viewBox.x, viewBox.y, 1.145 + viewBox.width, viewBox.height); 1.146 + nsSVGUtils::SetClipRect(gfx, GetCanvasTM(nsISVGChildFrame::FOR_PAINTING), 1.147 + clipRect); 1.148 + } 1.149 + 1.150 + nsIFrame* kid = GetAnonymousChildFrame(this); 1.151 + nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); 1.152 + // The CTM of each frame referencing us may be different. 1.153 + SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED); 1.154 + nsSVGUtils::PaintFrameWithEffects(aContext, nullptr, kid); 1.155 + 1.156 + if (StyleDisplay()->IsScrollableOverflow()) 1.157 + gfx->Restore(); 1.158 + 1.159 + return NS_OK; 1.160 +} 1.161 + 1.162 +SVGBBox 1.163 +nsSVGMarkerFrame::GetMarkBBoxContribution(const Matrix &aToBBoxUserspace, 1.164 + uint32_t aFlags, 1.165 + nsSVGPathGeometryFrame *aMarkedFrame, 1.166 + const nsSVGMark *aMark, 1.167 + float aStrokeWidth) 1.168 +{ 1.169 + SVGBBox bbox; 1.170 + 1.171 + // If the flag is set when we get here, it means this marker frame 1.172 + // has already been used in calculating the current mark bbox, and 1.173 + // the document has a marker reference loop. 1.174 + if (mInUse) 1.175 + return bbox; 1.176 + 1.177 + AutoMarkerReferencer markerRef(this, aMarkedFrame); 1.178 + 1.179 + SVGMarkerElement *content = static_cast<SVGMarkerElement*>(mContent); 1.180 + if (!content->HasValidDimensions()) { 1.181 + return bbox; 1.182 + } 1.183 + 1.184 + const nsSVGViewBoxRect viewBox = content->GetViewBoxRect(); 1.185 + 1.186 + if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) { 1.187 + return bbox; 1.188 + } 1.189 + 1.190 + mStrokeWidth = aStrokeWidth; 1.191 + mX = aMark->x; 1.192 + mY = aMark->y; 1.193 + mAutoAngle = aMark->angle; 1.194 + mIsStart = aMark->type == nsSVGMark::eStart; 1.195 + 1.196 + Matrix markerTM = 1.197 + content->GetMarkerTransform(mStrokeWidth, mX, mY, mAutoAngle, mIsStart); 1.198 + Matrix viewBoxTM = content->GetViewBoxTransform(); 1.199 + 1.200 + Matrix tm = viewBoxTM * markerTM * aToBBoxUserspace; 1.201 + 1.202 + nsISVGChildFrame* child = do_QueryFrame(GetAnonymousChildFrame(this)); 1.203 + // When we're being called to obtain the invalidation area, we need to 1.204 + // pass down all the flags so that stroke is included. However, once DOM 1.205 + // getBBox() accepts flags, maybe we should strip some of those here? 1.206 + 1.207 + // We need to include zero width/height vertical/horizontal lines, so we have 1.208 + // to use UnionEdges. 1.209 + bbox.UnionEdges(child->GetBBoxContribution(tm, aFlags)); 1.210 + 1.211 + return bbox; 1.212 +} 1.213 + 1.214 +void 1.215 +nsSVGMarkerFrame::SetParentCoordCtxProvider(SVGSVGElement *aContext) 1.216 +{ 1.217 + SVGMarkerElement *marker = static_cast<SVGMarkerElement*>(mContent); 1.218 + marker->SetParentCoordCtxProvider(aContext); 1.219 +} 1.220 + 1.221 +//---------------------------------------------------------------------- 1.222 +// helper class 1.223 + 1.224 +nsSVGMarkerFrame::AutoMarkerReferencer::AutoMarkerReferencer( 1.225 + nsSVGMarkerFrame *aFrame, 1.226 + nsSVGPathGeometryFrame *aMarkedFrame 1.227 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) 1.228 + : mFrame(aFrame) 1.229 +{ 1.230 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.231 + mFrame->mInUse = true; 1.232 + mFrame->mMarkedFrame = aMarkedFrame; 1.233 + 1.234 + SVGSVGElement *ctx = 1.235 + static_cast<nsSVGElement*>(aMarkedFrame->GetContent())->GetCtx(); 1.236 + mFrame->SetParentCoordCtxProvider(ctx); 1.237 +} 1.238 + 1.239 +nsSVGMarkerFrame::AutoMarkerReferencer::~AutoMarkerReferencer() 1.240 +{ 1.241 + mFrame->SetParentCoordCtxProvider(nullptr); 1.242 + 1.243 + mFrame->mMarkedFrame = nullptr; 1.244 + mFrame->mInUse = false; 1.245 +} 1.246 + 1.247 +//---------------------------------------------------------------------- 1.248 +// Implementation of nsSVGMarkerAnonChildFrame 1.249 + 1.250 +nsIFrame* 1.251 +NS_NewSVGMarkerAnonChildFrame(nsIPresShell* aPresShell, 1.252 + nsStyleContext* aContext) 1.253 +{ 1.254 + return new (aPresShell) nsSVGMarkerAnonChildFrame(aContext); 1.255 +} 1.256 + 1.257 +NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerAnonChildFrame) 1.258 + 1.259 +#ifdef DEBUG 1.260 +void 1.261 +nsSVGMarkerAnonChildFrame::Init(nsIContent* aContent, 1.262 + nsIFrame* aParent, 1.263 + nsIFrame* aPrevInFlow) 1.264 +{ 1.265 + NS_ABORT_IF_FALSE(aParent->GetType() == nsGkAtoms::svgMarkerFrame, 1.266 + "Unexpected parent"); 1.267 + nsSVGMarkerAnonChildFrameBase::Init(aContent, aParent, aPrevInFlow); 1.268 +} 1.269 +#endif 1.270 + 1.271 +nsIAtom * 1.272 +nsSVGMarkerAnonChildFrame::GetType() const 1.273 +{ 1.274 + return nsGkAtoms::svgMarkerAnonChildFrame; 1.275 +}