SVGMarkerFrame.cpp (8658B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 // Main header first: 8 #include "SVGMarkerFrame.h" 9 10 // Keep others in (case-insensitive) order: 11 #include "gfxContext.h" 12 #include "mozilla/PresShell.h" 13 #include "mozilla/SVGContextPaint.h" 14 #include "mozilla/SVGGeometryFrame.h" 15 #include "mozilla/SVGObserverUtils.h" 16 #include "mozilla/SVGUtils.h" 17 #include "mozilla/dom/SVGGeometryElement.h" 18 #include "mozilla/dom/SVGMarkerElement.h" 19 20 using namespace mozilla::dom; 21 using namespace mozilla::gfx; 22 using namespace mozilla::image; 23 24 nsContainerFrame* NS_NewSVGMarkerFrame(mozilla::PresShell* aPresShell, 25 mozilla::ComputedStyle* aStyle) { 26 return new (aPresShell) 27 mozilla::SVGMarkerFrame(aStyle, aPresShell->GetPresContext()); 28 } 29 30 namespace mozilla { 31 32 NS_IMPL_FRAMEARENA_HELPERS(SVGMarkerFrame) 33 34 //---------------------------------------------------------------------- 35 // nsIFrame methods: 36 37 nsresult SVGMarkerFrame::AttributeChanged(int32_t aNameSpaceID, 38 nsAtom* aAttribute, 39 AttrModType aModType) { 40 if (aNameSpaceID == kNameSpaceID_None && 41 (aAttribute == nsGkAtoms::markerUnits || aAttribute == nsGkAtoms::refX || 42 aAttribute == nsGkAtoms::refY || aAttribute == nsGkAtoms::markerWidth || 43 aAttribute == nsGkAtoms::markerHeight || 44 aAttribute == nsGkAtoms::orient || 45 aAttribute == nsGkAtoms::preserveAspectRatio || 46 aAttribute == nsGkAtoms::viewBox)) { 47 SVGObserverUtils::InvalidateRenderingObservers(this); 48 } 49 50 return SVGContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, 51 aModType); 52 } 53 54 #ifdef DEBUG 55 void SVGMarkerFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, 56 nsIFrame* aPrevInFlow) { 57 NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::marker), 58 "Content is not an SVG marker"); 59 60 SVGContainerFrame::Init(aContent, aParent, aPrevInFlow); 61 } 62 #endif /* DEBUG */ 63 64 //---------------------------------------------------------------------- 65 // SVGContainerFrame methods: 66 67 gfxMatrix SVGMarkerFrame::GetCanvasTM() { 68 NS_ASSERTION(mMarkedFrame, "null SVGGeometry frame"); 69 70 if (mInUse2) { 71 // We're going to be bailing drawing the marker, so return an identity. 72 return gfxMatrix(); 73 } 74 75 SVGMarkerElement* content = static_cast<SVGMarkerElement*>(GetContent()); 76 77 mInUse2 = true; 78 gfxMatrix markedTM = mMarkedFrame->GetCanvasTM(); 79 mInUse2 = false; 80 81 Matrix viewBoxTM = content->GetViewBoxTransform(); 82 83 return ThebesMatrix(viewBoxTM * mMarkerTM) * markedTM; 84 } 85 86 static nsIFrame* GetAnonymousChildFrame(nsIFrame* aFrame) { 87 nsIFrame* kid = aFrame->PrincipalChildList().FirstChild(); 88 MOZ_ASSERT(kid && kid->IsSVGMarkerAnonChildFrame(), 89 "expected to find anonymous child of marker frame"); 90 return kid; 91 } 92 93 void SVGMarkerFrame::PaintMark(gfxContext& aContext, 94 const gfxMatrix& aToMarkedFrameUserSpace, 95 SVGGeometryFrame* aMarkedFrame, 96 const SVGMark& aMark, float aStrokeWidth, 97 imgDrawingParams& aImgParams) { 98 // If the flag is set when we get here, it means this marker frame 99 // has already been used painting the current mark, and the document 100 // has a marker reference loop. 101 if (mInUse) { 102 return; 103 } 104 105 AutoMarkerReferencer markerRef(this, aMarkedFrame); 106 107 SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(GetContent()); 108 if (!marker->HasValidDimensions()) { 109 return; 110 } 111 112 const SVGViewBox viewBox = marker->GetViewBox(); 113 114 if (!viewBox.IsValid()) { 115 // We must disable rendering if the viewBox width or height are zero. 116 return; 117 } 118 119 Matrix viewBoxTM = marker->GetViewBoxTransform(); 120 121 mMarkerTM = marker->GetMarkerTransform(aStrokeWidth, aMark); 122 123 gfxMatrix markTM = ThebesMatrix(viewBoxTM) * ThebesMatrix(mMarkerTM) * 124 aToMarkedFrameUserSpace; 125 126 gfxClipAutoSaveRestore autoSaveClip(&aContext); 127 if (StyleDisplay()->IsScrollableOverflow()) { 128 gfxRect clipRect = SVGUtils::GetClipRectForFrame( 129 this, viewBox.x, viewBox.y, viewBox.width, viewBox.height); 130 autoSaveClip.TransformedClip(markTM, clipRect); 131 } 132 133 nsIFrame* kid = GetAnonymousChildFrame(this); 134 ISVGDisplayableFrame* SVGFrame = do_QueryFrame(kid); 135 // The CTM of each frame referencing us may be different. 136 SVGFrame->NotifySVGChanged( 137 ISVGDisplayableFrame::ChangeFlag::TransformChanged); 138 auto contextPaint = MakeRefPtr<SVGContextPaintImpl>(); 139 contextPaint->Init(aContext.GetDrawTarget(), 140 aToMarkedFrameUserSpace * aContext.CurrentMatrixDouble(), 141 aMarkedFrame, SVGContextPaint::GetContextPaint(marker), 142 aImgParams); 143 AutoSetRestoreSVGContextPaint autoSetRestore(contextPaint, 144 marker->OwnerDoc()); 145 SVGUtils::PaintFrameWithEffects(kid, aContext, markTM, aImgParams); 146 } 147 148 SVGBBox SVGMarkerFrame::GetMarkBBoxContribution(const Matrix& aToBBoxUserspace, 149 uint32_t aFlags, 150 SVGGeometryFrame* aMarkedFrame, 151 const SVGMark& aMark, 152 float aStrokeWidth) { 153 SVGBBox bbox; 154 155 // If the flag is set when we get here, it means this marker frame 156 // has already been used in calculating the current mark bbox, and 157 // the document has a marker reference loop. 158 if (mInUse) { 159 return bbox; 160 } 161 162 AutoMarkerReferencer markerRef(this, aMarkedFrame); 163 164 SVGMarkerElement* content = static_cast<SVGMarkerElement*>(GetContent()); 165 if (!content->HasValidDimensions()) { 166 return bbox; 167 } 168 169 const SVGViewBox viewBox = content->GetViewBox(); 170 171 if (!viewBox.IsValid()) { 172 return bbox; 173 } 174 175 mMarkerTM = content->GetMarkerTransform(aStrokeWidth, aMark); 176 Matrix viewBoxTM = content->GetViewBoxTransform(); 177 178 Matrix tm = viewBoxTM * mMarkerTM * aToBBoxUserspace; 179 180 ISVGDisplayableFrame* child = do_QueryFrame(GetAnonymousChildFrame(this)); 181 // When we're being called to obtain the invalidation area, we need to 182 // pass down all the flags so that stroke is included. However, once DOM 183 // getBBox() accepts flags, maybe we should strip some of those here? 184 185 // We need to include zero width/height vertical/horizontal lines, so we have 186 // to use UnionEdges. 187 bbox.UnionEdges(child->GetBBoxContribution(tm, aFlags)); 188 189 return bbox; 190 } 191 192 void SVGMarkerFrame::SetParentCoordCtxProvider(SVGViewportElement* aContext) { 193 SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(GetContent()); 194 marker->SetParentCoordCtxProvider(aContext); 195 } 196 197 void SVGMarkerFrame::AppendDirectlyOwnedAnonBoxes( 198 nsTArray<OwnedAnonBox>& aResult) { 199 aResult.AppendElement(OwnedAnonBox(GetAnonymousChildFrame(this))); 200 } 201 202 //---------------------------------------------------------------------- 203 // helper class 204 205 SVGMarkerFrame::AutoMarkerReferencer::AutoMarkerReferencer( 206 SVGMarkerFrame* aFrame, SVGGeometryFrame* aMarkedFrame) 207 : mFrame(aFrame) { 208 mFrame->mInUse = true; 209 mFrame->mMarkedFrame = aMarkedFrame; 210 211 SVGViewportElement* ctx = 212 static_cast<SVGElement*>(aMarkedFrame->GetContent())->GetCtx(); 213 mFrame->SetParentCoordCtxProvider(ctx); 214 } 215 216 SVGMarkerFrame::AutoMarkerReferencer::~AutoMarkerReferencer() { 217 mFrame->SetParentCoordCtxProvider(nullptr); 218 219 mFrame->mMarkedFrame = nullptr; 220 mFrame->mInUse = false; 221 } 222 223 } // namespace mozilla 224 225 //---------------------------------------------------------------------- 226 // Implementation of SVGMarkerAnonChildFrame 227 228 nsContainerFrame* NS_NewSVGMarkerAnonChildFrame( 229 mozilla::PresShell* aPresShell, mozilla::ComputedStyle* aStyle) { 230 return new (aPresShell) 231 mozilla::SVGMarkerAnonChildFrame(aStyle, aPresShell->GetPresContext()); 232 } 233 234 namespace mozilla { 235 236 NS_IMPL_FRAMEARENA_HELPERS(SVGMarkerAnonChildFrame) 237 238 #ifdef DEBUG 239 void SVGMarkerAnonChildFrame::Init(nsIContent* aContent, 240 nsContainerFrame* aParent, 241 nsIFrame* aPrevInFlow) { 242 MOZ_ASSERT(aParent->IsSVGMarkerFrame(), "Unexpected parent"); 243 SVGDisplayContainerFrame::Init(aContent, aParent, aPrevInFlow); 244 } 245 #endif 246 247 } // namespace mozilla