Line data Source code
1 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 : #include "mozilla/dom/SVGPathElement.h"
7 :
8 : #include <algorithm>
9 :
10 : #include "DOMSVGPathSeg.h"
11 : #include "DOMSVGPathSegList.h"
12 : #include "gfx2DGlue.h"
13 : #include "gfxPlatform.h"
14 : #include "mozilla/dom/SVGPathElementBinding.h"
15 : #include "mozilla/gfx/2D.h"
16 : #include "mozilla/RefPtr.h"
17 : #include "nsCOMPtr.h"
18 : #include "nsComputedDOMStyle.h"
19 : #include "nsGkAtoms.h"
20 : #include "nsStyleConsts.h"
21 : #include "nsStyleStruct.h"
22 : #include "SVGContentUtils.h"
23 :
24 70 : NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Path)
25 :
26 : using namespace mozilla::gfx;
27 :
28 : namespace mozilla {
29 : namespace dom {
30 :
31 : JSObject*
32 0 : SVGPathElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
33 : {
34 0 : return SVGPathElementBinding::Wrap(aCx, this, aGivenProto);
35 : }
36 :
37 : //----------------------------------------------------------------------
38 : // Implementation
39 :
40 44 : SVGPathElement::SVGPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
41 44 : : SVGPathElementBase(aNodeInfo)
42 : {
43 44 : }
44 :
45 : //----------------------------------------------------------------------
46 : // memory reporting methods
47 :
48 : size_t
49 33 : SVGPathElement::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
50 : {
51 33 : return SVGPathElementBase::SizeOfExcludingThis(aMallocSizeOf) +
52 33 : mD.SizeOfExcludingThis(aMallocSizeOf);
53 : }
54 :
55 : //----------------------------------------------------------------------
56 : // nsIDOMNode methods
57 :
58 18 : NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGPathElement)
59 :
60 : uint32_t
61 0 : SVGPathElement::GetPathSegAtLength(float distance)
62 : {
63 0 : return mD.GetAnimValue().GetPathSegAtLength(distance);
64 : }
65 :
66 : already_AddRefed<DOMSVGPathSegClosePath>
67 0 : SVGPathElement::CreateSVGPathSegClosePath()
68 : {
69 0 : RefPtr<DOMSVGPathSegClosePath> pathSeg = new DOMSVGPathSegClosePath();
70 0 : return pathSeg.forget();
71 : }
72 :
73 : already_AddRefed<DOMSVGPathSegMovetoAbs>
74 0 : SVGPathElement::CreateSVGPathSegMovetoAbs(float x, float y)
75 : {
76 0 : RefPtr<DOMSVGPathSegMovetoAbs> pathSeg = new DOMSVGPathSegMovetoAbs(x, y);
77 0 : return pathSeg.forget();
78 : }
79 :
80 : already_AddRefed<DOMSVGPathSegMovetoRel>
81 0 : SVGPathElement::CreateSVGPathSegMovetoRel(float x, float y)
82 : {
83 0 : RefPtr<DOMSVGPathSegMovetoRel> pathSeg = new DOMSVGPathSegMovetoRel(x, y);
84 0 : return pathSeg.forget();
85 : }
86 :
87 : already_AddRefed<DOMSVGPathSegLinetoAbs>
88 0 : SVGPathElement::CreateSVGPathSegLinetoAbs(float x, float y)
89 : {
90 0 : RefPtr<DOMSVGPathSegLinetoAbs> pathSeg = new DOMSVGPathSegLinetoAbs(x, y);
91 0 : return pathSeg.forget();
92 : }
93 :
94 : already_AddRefed<DOMSVGPathSegLinetoRel>
95 0 : SVGPathElement::CreateSVGPathSegLinetoRel(float x, float y)
96 : {
97 0 : RefPtr<DOMSVGPathSegLinetoRel> pathSeg = new DOMSVGPathSegLinetoRel(x, y);
98 0 : return pathSeg.forget();
99 : }
100 :
101 : already_AddRefed<DOMSVGPathSegCurvetoCubicAbs>
102 0 : SVGPathElement::CreateSVGPathSegCurvetoCubicAbs(float x, float y, float x1, float y1, float x2, float y2)
103 : {
104 : // Note that we swap from DOM API argument order to the argument order used
105 : // in the <path> element's 'd' attribute (i.e. we put the arguments for the
106 : // end point of the segment last instead of first).
107 : RefPtr<DOMSVGPathSegCurvetoCubicAbs> pathSeg =
108 0 : new DOMSVGPathSegCurvetoCubicAbs(x1, y1, x2, y2, x, y);
109 0 : return pathSeg.forget();
110 : }
111 :
112 : already_AddRefed<DOMSVGPathSegCurvetoCubicRel>
113 0 : SVGPathElement::CreateSVGPathSegCurvetoCubicRel(float x, float y, float x1, float y1, float x2, float y2)
114 : {
115 : // See comment in CreateSVGPathSegCurvetoCubicAbs
116 : RefPtr<DOMSVGPathSegCurvetoCubicRel> pathSeg =
117 0 : new DOMSVGPathSegCurvetoCubicRel(x1, y1, x2, y2, x, y);
118 0 : return pathSeg.forget();
119 : }
120 :
121 : already_AddRefed<DOMSVGPathSegCurvetoQuadraticAbs>
122 0 : SVGPathElement::CreateSVGPathSegCurvetoQuadraticAbs(float x, float y, float x1, float y1)
123 : {
124 : // See comment in CreateSVGPathSegCurvetoCubicAbs
125 : RefPtr<DOMSVGPathSegCurvetoQuadraticAbs> pathSeg =
126 0 : new DOMSVGPathSegCurvetoQuadraticAbs(x1, y1, x, y);
127 0 : return pathSeg.forget();
128 : }
129 :
130 : already_AddRefed<DOMSVGPathSegCurvetoQuadraticRel>
131 0 : SVGPathElement::CreateSVGPathSegCurvetoQuadraticRel(float x, float y, float x1, float y1)
132 : {
133 : // See comment in CreateSVGPathSegCurvetoCubicAbs
134 : RefPtr<DOMSVGPathSegCurvetoQuadraticRel> pathSeg =
135 0 : new DOMSVGPathSegCurvetoQuadraticRel(x1, y1, x, y);
136 0 : return pathSeg.forget();
137 : }
138 :
139 : already_AddRefed<DOMSVGPathSegArcAbs>
140 0 : SVGPathElement::CreateSVGPathSegArcAbs(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag)
141 : {
142 : // See comment in CreateSVGPathSegCurvetoCubicAbs
143 : RefPtr<DOMSVGPathSegArcAbs> pathSeg =
144 0 : new DOMSVGPathSegArcAbs(r1, r2, angle, largeArcFlag, sweepFlag, x, y);
145 0 : return pathSeg.forget();
146 : }
147 :
148 : already_AddRefed<DOMSVGPathSegArcRel>
149 0 : SVGPathElement::CreateSVGPathSegArcRel(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag)
150 : {
151 : // See comment in CreateSVGPathSegCurvetoCubicAbs
152 : RefPtr<DOMSVGPathSegArcRel> pathSeg =
153 0 : new DOMSVGPathSegArcRel(r1, r2, angle, largeArcFlag, sweepFlag, x, y);
154 0 : return pathSeg.forget();
155 : }
156 :
157 : already_AddRefed<DOMSVGPathSegLinetoHorizontalAbs>
158 0 : SVGPathElement::CreateSVGPathSegLinetoHorizontalAbs(float x)
159 : {
160 : RefPtr<DOMSVGPathSegLinetoHorizontalAbs> pathSeg =
161 0 : new DOMSVGPathSegLinetoHorizontalAbs(x);
162 0 : return pathSeg.forget();
163 : }
164 :
165 : already_AddRefed<DOMSVGPathSegLinetoHorizontalRel>
166 0 : SVGPathElement::CreateSVGPathSegLinetoHorizontalRel(float x)
167 : {
168 : RefPtr<DOMSVGPathSegLinetoHorizontalRel> pathSeg =
169 0 : new DOMSVGPathSegLinetoHorizontalRel(x);
170 0 : return pathSeg.forget();
171 : }
172 :
173 : already_AddRefed<DOMSVGPathSegLinetoVerticalAbs>
174 0 : SVGPathElement::CreateSVGPathSegLinetoVerticalAbs(float y)
175 : {
176 : RefPtr<DOMSVGPathSegLinetoVerticalAbs> pathSeg =
177 0 : new DOMSVGPathSegLinetoVerticalAbs(y);
178 0 : return pathSeg.forget();
179 : }
180 :
181 : already_AddRefed<DOMSVGPathSegLinetoVerticalRel>
182 0 : SVGPathElement::CreateSVGPathSegLinetoVerticalRel(float y)
183 : {
184 : RefPtr<DOMSVGPathSegLinetoVerticalRel> pathSeg =
185 0 : new DOMSVGPathSegLinetoVerticalRel(y);
186 0 : return pathSeg.forget();
187 : }
188 :
189 : already_AddRefed<DOMSVGPathSegCurvetoCubicSmoothAbs>
190 0 : SVGPathElement::CreateSVGPathSegCurvetoCubicSmoothAbs(float x, float y, float x2, float y2)
191 : {
192 : // See comment in CreateSVGPathSegCurvetoCubicAbs
193 : RefPtr<DOMSVGPathSegCurvetoCubicSmoothAbs> pathSeg =
194 0 : new DOMSVGPathSegCurvetoCubicSmoothAbs(x2, y2, x, y);
195 0 : return pathSeg.forget();
196 : }
197 :
198 : already_AddRefed<DOMSVGPathSegCurvetoCubicSmoothRel>
199 0 : SVGPathElement::CreateSVGPathSegCurvetoCubicSmoothRel(float x, float y, float x2, float y2)
200 : {
201 : // See comment in CreateSVGPathSegCurvetoCubicAbs
202 : RefPtr<DOMSVGPathSegCurvetoCubicSmoothRel> pathSeg =
203 0 : new DOMSVGPathSegCurvetoCubicSmoothRel(x2, y2, x, y);
204 0 : return pathSeg.forget();
205 : }
206 :
207 : already_AddRefed<DOMSVGPathSegCurvetoQuadraticSmoothAbs>
208 0 : SVGPathElement::CreateSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y)
209 : {
210 : RefPtr<DOMSVGPathSegCurvetoQuadraticSmoothAbs> pathSeg =
211 0 : new DOMSVGPathSegCurvetoQuadraticSmoothAbs(x, y);
212 0 : return pathSeg.forget();
213 : }
214 :
215 : already_AddRefed<DOMSVGPathSegCurvetoQuadraticSmoothRel>
216 0 : SVGPathElement::CreateSVGPathSegCurvetoQuadraticSmoothRel(float x, float y)
217 : {
218 : RefPtr<DOMSVGPathSegCurvetoQuadraticSmoothRel> pathSeg =
219 0 : new DOMSVGPathSegCurvetoQuadraticSmoothRel(x, y);
220 0 : return pathSeg.forget();
221 : }
222 :
223 : already_AddRefed<DOMSVGPathSegList>
224 0 : SVGPathElement::PathSegList()
225 : {
226 0 : return DOMSVGPathSegList::GetDOMWrapper(mD.GetBaseValKey(), this, false);
227 : }
228 :
229 : already_AddRefed<DOMSVGPathSegList>
230 0 : SVGPathElement::AnimatedPathSegList()
231 : {
232 0 : return DOMSVGPathSegList::GetDOMWrapper(mD.GetAnimValKey(), this, true);
233 : }
234 :
235 : //----------------------------------------------------------------------
236 : // nsSVGElement methods
237 :
238 : /* virtual */ bool
239 14 : SVGPathElement::HasValidDimensions() const
240 : {
241 14 : return !mD.GetAnimValue().IsEmpty();
242 : }
243 :
244 : //----------------------------------------------------------------------
245 : // nsIContent methods
246 :
247 : NS_IMETHODIMP_(bool)
248 289 : SVGPathElement::IsAttributeMapped(const nsIAtom* name) const
249 : {
250 : static const MappedAttributeEntry* const map[] = {
251 : sMarkersMap
252 : };
253 :
254 578 : return FindAttributeDependence(name, map) ||
255 578 : SVGPathElementBase::IsAttributeMapped(name);
256 : }
257 :
258 : already_AddRefed<Path>
259 0 : SVGPathElement::GetOrBuildPathForMeasuring()
260 : {
261 0 : return mD.GetAnimValue().BuildPathForMeasuring();
262 : }
263 :
264 : //----------------------------------------------------------------------
265 : // SVGGeometryElement methods
266 :
267 : bool
268 0 : SVGPathElement::AttributeDefinesGeometry(const nsIAtom *aName)
269 : {
270 0 : return aName == nsGkAtoms::d ||
271 0 : aName == nsGkAtoms::pathLength;
272 : }
273 :
274 : bool
275 320 : SVGPathElement::IsMarkable()
276 : {
277 320 : return true;
278 : }
279 :
280 : void
281 0 : SVGPathElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks)
282 : {
283 0 : mD.GetAnimValue().GetMarkerPositioningData(aMarks);
284 0 : }
285 :
286 : float
287 0 : SVGPathElement::GetPathLengthScale(PathLengthScaleForType aFor)
288 : {
289 0 : MOZ_ASSERT(aFor == eForTextPath || aFor == eForStroking,
290 : "Unknown enum");
291 0 : if (mPathLength.IsExplicitlySet()) {
292 0 : float authorsPathLengthEstimate = mPathLength.GetAnimValue();
293 0 : if (authorsPathLengthEstimate > 0) {
294 0 : RefPtr<Path> path = GetOrBuildPathForMeasuring();
295 0 : if (!path) {
296 : // The path is empty or invalid so its length must be zero and
297 : // we know that 0 / authorsPathLengthEstimate = 0.
298 0 : return 0.0;
299 : }
300 0 : if (aFor == eForTextPath) {
301 : // For textPath, a transform on the referenced path affects the
302 : // textPath layout, so when calculating the actual path length
303 : // we need to take that into account.
304 0 : gfxMatrix matrix = PrependLocalTransformsTo(gfxMatrix());
305 0 : if (!matrix.IsIdentity()) {
306 : RefPtr<PathBuilder> builder =
307 0 : path->TransformedCopyToBuilder(ToMatrix(matrix));
308 0 : path = builder->Finish();
309 : }
310 : }
311 0 : return path->ComputeLength() / authorsPathLengthEstimate;
312 : }
313 : }
314 0 : return 1.0;
315 : }
316 :
317 : already_AddRefed<Path>
318 28 : SVGPathElement::BuildPath(PathBuilder* aBuilder)
319 : {
320 : // The Moz2D PathBuilder that our SVGPathData will be using only cares about
321 : // the fill rule. However, in order to fulfill the requirements of the SVG
322 : // spec regarding zero length sub-paths when square line caps are in use,
323 : // SVGPathData needs to know our stroke-linecap style and, if "square", then
324 : // also our stroke width. See the comment for
325 : // ApproximateZeroLengthSubpathSquareCaps for more info.
326 :
327 28 : uint8_t strokeLineCap = NS_STYLE_STROKE_LINECAP_BUTT;
328 28 : Float strokeWidth = 0;
329 :
330 : RefPtr<nsStyleContext> styleContext =
331 56 : nsComputedDOMStyle::GetStyleContextNoFlush(this, nullptr, nullptr);
332 28 : if (styleContext) {
333 28 : const nsStyleSVG* style = styleContext->StyleSVG();
334 : // Note: the path that we return may be used for hit-testing, and SVG
335 : // exposes hit-testing of strokes that are not actually painted. For that
336 : // reason we do not check for eStyleSVGPaintType_None or check the stroke
337 : // opacity here.
338 28 : if (style->mStrokeLinecap != NS_STYLE_STROKE_LINECAP_BUTT) {
339 0 : strokeLineCap = style->mStrokeLinecap;
340 0 : strokeWidth = SVGContentUtils::GetStrokeWidth(this, styleContext, nullptr);
341 : }
342 : }
343 :
344 56 : return mD.GetAnimValue().BuildPath(aBuilder, strokeLineCap, strokeWidth);
345 : }
346 :
347 : } // namespace dom
348 : } // namespace mozilla
|