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