Line data Source code
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/. */
5 :
6 : // Main header first:
7 : #include "nsSVGMarkerFrame.h"
8 :
9 : // Keep others in (case-insensitive) order:
10 : #include "gfxContext.h"
11 : #include "nsSVGEffects.h"
12 : #include "mozilla/dom/SVGMarkerElement.h"
13 : #include "SVGGeometryElement.h"
14 : #include "SVGGeometryFrame.h"
15 :
16 : using namespace mozilla::dom;
17 : using namespace mozilla::gfx;
18 : using namespace mozilla::image;
19 :
20 : nsContainerFrame*
21 0 : NS_NewSVGMarkerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
22 : {
23 0 : return new (aPresShell) nsSVGMarkerFrame(aContext);
24 : }
25 :
26 0 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerFrame)
27 :
28 : //----------------------------------------------------------------------
29 : // nsIFrame methods:
30 :
31 : nsresult
32 0 : nsSVGMarkerFrame::AttributeChanged(int32_t aNameSpaceID,
33 : nsIAtom* aAttribute,
34 : int32_t aModType)
35 : {
36 0 : if (aNameSpaceID == kNameSpaceID_None &&
37 0 : (aAttribute == nsGkAtoms::markerUnits ||
38 0 : aAttribute == nsGkAtoms::refX ||
39 0 : aAttribute == nsGkAtoms::refY ||
40 0 : aAttribute == nsGkAtoms::markerWidth ||
41 0 : aAttribute == nsGkAtoms::markerHeight ||
42 0 : aAttribute == nsGkAtoms::orient ||
43 0 : aAttribute == nsGkAtoms::preserveAspectRatio ||
44 0 : aAttribute == nsGkAtoms::viewBox)) {
45 0 : nsSVGEffects::InvalidateDirectRenderingObservers(this);
46 : }
47 :
48 0 : return nsSVGContainerFrame::AttributeChanged(aNameSpaceID,
49 0 : aAttribute, aModType);
50 : }
51 :
52 : #ifdef DEBUG
53 : void
54 0 : nsSVGMarkerFrame::Init(nsIContent* aContent,
55 : nsContainerFrame* aParent,
56 : nsIFrame* aPrevInFlow)
57 : {
58 0 : NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::marker), "Content is not an SVG marker");
59 :
60 0 : nsSVGContainerFrame::Init(aContent, aParent, aPrevInFlow);
61 0 : }
62 : #endif /* DEBUG */
63 :
64 : //----------------------------------------------------------------------
65 : // nsSVGContainerFrame methods:
66 :
67 : gfxMatrix
68 0 : nsSVGMarkerFrame::GetCanvasTM()
69 : {
70 0 : NS_ASSERTION(mMarkedFrame, "null SVGGeometry frame");
71 :
72 0 : if (mInUse2) {
73 : // We're going to be bailing drawing the marker, so return an identity.
74 0 : return gfxMatrix();
75 : }
76 :
77 0 : SVGMarkerElement *content = static_cast<SVGMarkerElement*>(mContent);
78 :
79 0 : mInUse2 = true;
80 0 : gfxMatrix markedTM = mMarkedFrame->GetCanvasTM();
81 0 : mInUse2 = false;
82 :
83 0 : Matrix viewBoxTM = content->GetViewBoxTransform();
84 :
85 0 : return ThebesMatrix(viewBoxTM * mMarkerTM) * markedTM;
86 : }
87 :
88 : static nsIFrame*
89 0 : GetAnonymousChildFrame(nsIFrame* aFrame)
90 : {
91 0 : nsIFrame* kid = aFrame->PrincipalChildList().FirstChild();
92 0 : MOZ_ASSERT(kid && kid->IsSVGMarkerAnonChildFrame(),
93 : "expected to find anonymous child of marker frame");
94 0 : return kid;
95 : }
96 :
97 : void
98 0 : nsSVGMarkerFrame::PaintMark(gfxContext& aContext,
99 : const gfxMatrix& aToMarkedFrameUserSpace,
100 : SVGGeometryFrame* aMarkedFrame,
101 : const nsSVGMark& aMark, float aStrokeWidth,
102 : imgDrawingParams& aImgParams)
103 : {
104 : // If the flag is set when we get here, it means this marker frame
105 : // has already been used painting the current mark, and the document
106 : // has a marker reference loop.
107 0 : if (mInUse) {
108 0 : return;
109 : }
110 :
111 0 : AutoMarkerReferencer markerRef(this, aMarkedFrame);
112 :
113 0 : SVGMarkerElement *marker = static_cast<SVGMarkerElement*>(mContent);
114 0 : if (!marker->HasValidDimensions()) {
115 0 : return;
116 : }
117 :
118 0 : const nsSVGViewBoxRect viewBox = marker->GetViewBoxRect();
119 :
120 0 : if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
121 : // We must disable rendering if the viewBox width or height are zero.
122 0 : return;
123 : }
124 :
125 0 : Matrix viewBoxTM = marker->GetViewBoxTransform();
126 :
127 0 : mMarkerTM = marker->GetMarkerTransform(aStrokeWidth, aMark);
128 :
129 0 : gfxMatrix markTM = ThebesMatrix(viewBoxTM) * ThebesMatrix(mMarkerTM) *
130 0 : aToMarkedFrameUserSpace;
131 :
132 0 : if (StyleDisplay()->IsScrollableOverflow()) {
133 0 : aContext.Save();
134 : gfxRect clipRect =
135 0 : nsSVGUtils::GetClipRectForFrame(this, viewBox.x, viewBox.y,
136 0 : viewBox.width, viewBox.height);
137 0 : nsSVGUtils::SetClipRect(&aContext, markTM, clipRect);
138 : }
139 :
140 :
141 0 : nsIFrame* kid = GetAnonymousChildFrame(this);
142 0 : nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
143 : // The CTM of each frame referencing us may be different.
144 0 : SVGFrame->NotifySVGChanged(nsSVGDisplayableFrame::TRANSFORM_CHANGED);
145 0 : nsSVGUtils::PaintFrameWithEffects(kid, aContext, markTM, aImgParams);
146 :
147 0 : if (StyleDisplay()->IsScrollableOverflow())
148 0 : aContext.Restore();
149 : }
150 :
151 : SVGBBox
152 0 : nsSVGMarkerFrame::GetMarkBBoxContribution(const Matrix& aToBBoxUserspace,
153 : uint32_t aFlags,
154 : SVGGeometryFrame* aMarkedFrame,
155 : const nsSVGMark& aMark,
156 : float aStrokeWidth)
157 : {
158 0 : SVGBBox bbox;
159 :
160 : // If the flag is set when we get here, it means this marker frame
161 : // has already been used in calculating the current mark bbox, and
162 : // the document has a marker reference loop.
163 0 : if (mInUse)
164 0 : return bbox;
165 :
166 0 : AutoMarkerReferencer markerRef(this, aMarkedFrame);
167 :
168 0 : SVGMarkerElement *content = static_cast<SVGMarkerElement*>(mContent);
169 0 : if (!content->HasValidDimensions()) {
170 0 : return bbox;
171 : }
172 :
173 0 : const nsSVGViewBoxRect viewBox = content->GetViewBoxRect();
174 :
175 0 : if (viewBox.width <= 0.0f || viewBox.height <= 0.0f) {
176 0 : return bbox;
177 : }
178 :
179 0 : mMarkerTM = content->GetMarkerTransform(aStrokeWidth, aMark);
180 0 : Matrix viewBoxTM = content->GetViewBoxTransform();
181 :
182 0 : Matrix tm = viewBoxTM * mMarkerTM * aToBBoxUserspace;
183 :
184 0 : nsSVGDisplayableFrame* child = do_QueryFrame(GetAnonymousChildFrame(this));
185 : // When we're being called to obtain the invalidation area, we need to
186 : // pass down all the flags so that stroke is included. However, once DOM
187 : // getBBox() accepts flags, maybe we should strip some of those here?
188 :
189 : // We need to include zero width/height vertical/horizontal lines, so we have
190 : // to use UnionEdges.
191 0 : bbox.UnionEdges(child->GetBBoxContribution(tm, aFlags));
192 :
193 0 : return bbox;
194 : }
195 :
196 : void
197 0 : nsSVGMarkerFrame::SetParentCoordCtxProvider(SVGSVGElement *aContext)
198 : {
199 0 : SVGMarkerElement *marker = static_cast<SVGMarkerElement*>(mContent);
200 0 : marker->SetParentCoordCtxProvider(aContext);
201 0 : }
202 :
203 : void
204 0 : nsSVGMarkerFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult)
205 : {
206 0 : aResult.AppendElement(OwnedAnonBox(GetAnonymousChildFrame(this)));
207 0 : }
208 :
209 : //----------------------------------------------------------------------
210 : // helper class
211 :
212 0 : nsSVGMarkerFrame::AutoMarkerReferencer::AutoMarkerReferencer(
213 : nsSVGMarkerFrame *aFrame,
214 : SVGGeometryFrame *aMarkedFrame
215 0 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
216 0 : : mFrame(aFrame)
217 : {
218 0 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
219 0 : mFrame->mInUse = true;
220 0 : mFrame->mMarkedFrame = aMarkedFrame;
221 :
222 : SVGSVGElement *ctx =
223 0 : static_cast<nsSVGElement*>(aMarkedFrame->GetContent())->GetCtx();
224 0 : mFrame->SetParentCoordCtxProvider(ctx);
225 0 : }
226 :
227 0 : nsSVGMarkerFrame::AutoMarkerReferencer::~AutoMarkerReferencer()
228 : {
229 0 : mFrame->SetParentCoordCtxProvider(nullptr);
230 :
231 0 : mFrame->mMarkedFrame = nullptr;
232 0 : mFrame->mInUse = false;
233 0 : }
234 :
235 : //----------------------------------------------------------------------
236 : // Implementation of nsSVGMarkerAnonChildFrame
237 :
238 : nsContainerFrame*
239 0 : NS_NewSVGMarkerAnonChildFrame(nsIPresShell* aPresShell,
240 : nsStyleContext* aContext)
241 : {
242 0 : return new (aPresShell) nsSVGMarkerAnonChildFrame(aContext);
243 : }
244 :
245 0 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGMarkerAnonChildFrame)
246 :
247 : #ifdef DEBUG
248 : void
249 0 : nsSVGMarkerAnonChildFrame::Init(nsIContent* aContent,
250 : nsContainerFrame* aParent,
251 : nsIFrame* aPrevInFlow)
252 : {
253 0 : MOZ_ASSERT(aParent->IsSVGMarkerFrame(), "Unexpected parent");
254 0 : nsSVGDisplayContainerFrame::Init(aContent, aParent, aPrevInFlow);
255 0 : }
256 : #endif
|