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/. */
6 #include "mozilla/dom/SVGPathElement.h"
10 #include "DOMSVGPathSeg.h"
11 #include "DOMSVGPathSegList.h"
12 #include "DOMSVGPoint.h"
13 #include "gfx2DGlue.h"
14 #include "gfxPlatform.h"
15 #include "mozilla/dom/SVGPathElementBinding.h"
16 #include "mozilla/gfx/2D.h"
17 #include "mozilla/RefPtr.h"
19 #include "nsComputedDOMStyle.h"
20 #include "nsGkAtoms.h"
21 #include "nsStyleConsts.h"
22 #include "nsStyleStruct.h"
23 #include "SVGContentUtils.h"
25 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Path
)
27 using namespace mozilla::gfx
;
33 SVGPathElement::WrapNode(JSContext
*aCx
)
35 return SVGPathElementBinding::Wrap(aCx
, this);
38 nsSVGElement::NumberInfo
SVGPathElement::sNumberInfo
=
39 { &nsGkAtoms::pathLength
, 0, false };
41 //----------------------------------------------------------------------
44 SVGPathElement::SVGPathElement(already_AddRefed
<mozilla::dom::NodeInfo
>& aNodeInfo
)
45 : SVGPathElementBase(aNodeInfo
)
49 //----------------------------------------------------------------------
50 // memory reporting methods
53 SVGPathElement::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf
) const
55 return SVGPathElementBase::SizeOfExcludingThis(aMallocSizeOf
) +
56 mD
.SizeOfExcludingThis(aMallocSizeOf
);
59 //----------------------------------------------------------------------
62 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGPathElement
)
64 already_AddRefed
<SVGAnimatedNumber
>
65 SVGPathElement::PathLength()
67 return mPathLength
.ToDOMAnimatedNumber(this);
71 SVGPathElement::GetTotalLength()
73 RefPtr
<Path
> flat
= GetOrBuildPathForMeasuring();
74 return flat
? flat
->ComputeLength() : 0.f
;
77 already_AddRefed
<nsISVGPoint
>
78 SVGPathElement::GetPointAtLength(float distance
, ErrorResult
& rv
)
80 RefPtr
<Path
> path
= GetOrBuildPathForMeasuring();
82 rv
.Throw(NS_ERROR_FAILURE
);
86 float totalLength
= path
->ComputeLength();
87 if (mPathLength
.IsExplicitlySet()) {
88 float pathLength
= mPathLength
.GetAnimValue();
89 if (pathLength
<= 0) {
90 rv
.Throw(NS_ERROR_FAILURE
);
93 distance
*= totalLength
/ pathLength
;
95 distance
= std::max(0.f
, distance
);
96 distance
= std::min(totalLength
, distance
);
98 nsCOMPtr
<nsISVGPoint
> point
=
99 new DOMSVGPoint(path
->ComputePointAtLength(distance
));
100 return point
.forget();
104 SVGPathElement::GetPathSegAtLength(float distance
)
106 return mD
.GetAnimValue().GetPathSegAtLength(distance
);
109 already_AddRefed
<DOMSVGPathSegClosePath
>
110 SVGPathElement::CreateSVGPathSegClosePath()
112 nsRefPtr
<DOMSVGPathSegClosePath
> pathSeg
= new DOMSVGPathSegClosePath();
113 return pathSeg
.forget();
116 already_AddRefed
<DOMSVGPathSegMovetoAbs
>
117 SVGPathElement::CreateSVGPathSegMovetoAbs(float x
, float y
)
119 nsRefPtr
<DOMSVGPathSegMovetoAbs
> pathSeg
= new DOMSVGPathSegMovetoAbs(x
, y
);
120 return pathSeg
.forget();
123 already_AddRefed
<DOMSVGPathSegMovetoRel
>
124 SVGPathElement::CreateSVGPathSegMovetoRel(float x
, float y
)
126 nsRefPtr
<DOMSVGPathSegMovetoRel
> pathSeg
= new DOMSVGPathSegMovetoRel(x
, y
);
127 return pathSeg
.forget();
130 already_AddRefed
<DOMSVGPathSegLinetoAbs
>
131 SVGPathElement::CreateSVGPathSegLinetoAbs(float x
, float y
)
133 nsRefPtr
<DOMSVGPathSegLinetoAbs
> pathSeg
= new DOMSVGPathSegLinetoAbs(x
, y
);
134 return pathSeg
.forget();
137 already_AddRefed
<DOMSVGPathSegLinetoRel
>
138 SVGPathElement::CreateSVGPathSegLinetoRel(float x
, float y
)
140 nsRefPtr
<DOMSVGPathSegLinetoRel
> pathSeg
= new DOMSVGPathSegLinetoRel(x
, y
);
141 return pathSeg
.forget();
144 already_AddRefed
<DOMSVGPathSegCurvetoCubicAbs
>
145 SVGPathElement::CreateSVGPathSegCurvetoCubicAbs(float x
, float y
, float x1
, float y1
, float x2
, float y2
)
147 // Note that we swap from DOM API argument order to the argument order used
148 // in the <path> element's 'd' attribute (i.e. we put the arguments for the
149 // end point of the segment last instead of first).
150 nsRefPtr
<DOMSVGPathSegCurvetoCubicAbs
> pathSeg
=
151 new DOMSVGPathSegCurvetoCubicAbs(x1
, y1
, x2
, y2
, x
, y
);
152 return pathSeg
.forget();
155 already_AddRefed
<DOMSVGPathSegCurvetoCubicRel
>
156 SVGPathElement::CreateSVGPathSegCurvetoCubicRel(float x
, float y
, float x1
, float y1
, float x2
, float y2
)
158 // See comment in CreateSVGPathSegCurvetoCubicAbs
159 nsRefPtr
<DOMSVGPathSegCurvetoCubicRel
> pathSeg
=
160 new DOMSVGPathSegCurvetoCubicRel(x1
, y1
, x2
, y2
, x
, y
);
161 return pathSeg
.forget();
164 already_AddRefed
<DOMSVGPathSegCurvetoQuadraticAbs
>
165 SVGPathElement::CreateSVGPathSegCurvetoQuadraticAbs(float x
, float y
, float x1
, float y1
)
167 // See comment in CreateSVGPathSegCurvetoCubicAbs
168 nsRefPtr
<DOMSVGPathSegCurvetoQuadraticAbs
> pathSeg
=
169 new DOMSVGPathSegCurvetoQuadraticAbs(x1
, y1
, x
, y
);
170 return pathSeg
.forget();
173 already_AddRefed
<DOMSVGPathSegCurvetoQuadraticRel
>
174 SVGPathElement::CreateSVGPathSegCurvetoQuadraticRel(float x
, float y
, float x1
, float y1
)
176 // See comment in CreateSVGPathSegCurvetoCubicAbs
177 nsRefPtr
<DOMSVGPathSegCurvetoQuadraticRel
> pathSeg
=
178 new DOMSVGPathSegCurvetoQuadraticRel(x1
, y1
, x
, y
);
179 return pathSeg
.forget();
182 already_AddRefed
<DOMSVGPathSegArcAbs
>
183 SVGPathElement::CreateSVGPathSegArcAbs(float x
, float y
, float r1
, float r2
, float angle
, bool largeArcFlag
, bool sweepFlag
)
185 // See comment in CreateSVGPathSegCurvetoCubicAbs
186 nsRefPtr
<DOMSVGPathSegArcAbs
> pathSeg
=
187 new DOMSVGPathSegArcAbs(r1
, r2
, angle
, largeArcFlag
, sweepFlag
, x
, y
);
188 return pathSeg
.forget();
191 already_AddRefed
<DOMSVGPathSegArcRel
>
192 SVGPathElement::CreateSVGPathSegArcRel(float x
, float y
, float r1
, float r2
, float angle
, bool largeArcFlag
, bool sweepFlag
)
194 // See comment in CreateSVGPathSegCurvetoCubicAbs
195 nsRefPtr
<DOMSVGPathSegArcRel
> pathSeg
=
196 new DOMSVGPathSegArcRel(r1
, r2
, angle
, largeArcFlag
, sweepFlag
, x
, y
);
197 return pathSeg
.forget();
200 already_AddRefed
<DOMSVGPathSegLinetoHorizontalAbs
>
201 SVGPathElement::CreateSVGPathSegLinetoHorizontalAbs(float x
)
203 nsRefPtr
<DOMSVGPathSegLinetoHorizontalAbs
> pathSeg
=
204 new DOMSVGPathSegLinetoHorizontalAbs(x
);
205 return pathSeg
.forget();
208 already_AddRefed
<DOMSVGPathSegLinetoHorizontalRel
>
209 SVGPathElement::CreateSVGPathSegLinetoHorizontalRel(float x
)
211 nsRefPtr
<DOMSVGPathSegLinetoHorizontalRel
> pathSeg
=
212 new DOMSVGPathSegLinetoHorizontalRel(x
);
213 return pathSeg
.forget();
216 already_AddRefed
<DOMSVGPathSegLinetoVerticalAbs
>
217 SVGPathElement::CreateSVGPathSegLinetoVerticalAbs(float y
)
219 nsRefPtr
<DOMSVGPathSegLinetoVerticalAbs
> pathSeg
=
220 new DOMSVGPathSegLinetoVerticalAbs(y
);
221 return pathSeg
.forget();
224 already_AddRefed
<DOMSVGPathSegLinetoVerticalRel
>
225 SVGPathElement::CreateSVGPathSegLinetoVerticalRel(float y
)
227 nsRefPtr
<DOMSVGPathSegLinetoVerticalRel
> pathSeg
=
228 new DOMSVGPathSegLinetoVerticalRel(y
);
229 return pathSeg
.forget();
232 already_AddRefed
<DOMSVGPathSegCurvetoCubicSmoothAbs
>
233 SVGPathElement::CreateSVGPathSegCurvetoCubicSmoothAbs(float x
, float y
, float x2
, float y2
)
235 // See comment in CreateSVGPathSegCurvetoCubicAbs
236 nsRefPtr
<DOMSVGPathSegCurvetoCubicSmoothAbs
> pathSeg
=
237 new DOMSVGPathSegCurvetoCubicSmoothAbs(x2
, y2
, x
, y
);
238 return pathSeg
.forget();
241 already_AddRefed
<DOMSVGPathSegCurvetoCubicSmoothRel
>
242 SVGPathElement::CreateSVGPathSegCurvetoCubicSmoothRel(float x
, float y
, float x2
, float y2
)
244 // See comment in CreateSVGPathSegCurvetoCubicAbs
245 nsRefPtr
<DOMSVGPathSegCurvetoCubicSmoothRel
> pathSeg
=
246 new DOMSVGPathSegCurvetoCubicSmoothRel(x2
, y2
, x
, y
);
247 return pathSeg
.forget();
250 already_AddRefed
<DOMSVGPathSegCurvetoQuadraticSmoothAbs
>
251 SVGPathElement::CreateSVGPathSegCurvetoQuadraticSmoothAbs(float x
, float y
)
253 nsRefPtr
<DOMSVGPathSegCurvetoQuadraticSmoothAbs
> pathSeg
=
254 new DOMSVGPathSegCurvetoQuadraticSmoothAbs(x
, y
);
255 return pathSeg
.forget();
258 already_AddRefed
<DOMSVGPathSegCurvetoQuadraticSmoothRel
>
259 SVGPathElement::CreateSVGPathSegCurvetoQuadraticSmoothRel(float x
, float y
)
261 nsRefPtr
<DOMSVGPathSegCurvetoQuadraticSmoothRel
> pathSeg
=
262 new DOMSVGPathSegCurvetoQuadraticSmoothRel(x
, y
);
263 return pathSeg
.forget();
266 already_AddRefed
<DOMSVGPathSegList
>
267 SVGPathElement::PathSegList()
269 return DOMSVGPathSegList::GetDOMWrapper(mD
.GetBaseValKey(), this, false);
272 already_AddRefed
<DOMSVGPathSegList
>
273 SVGPathElement::AnimatedPathSegList()
275 return DOMSVGPathSegList::GetDOMWrapper(mD
.GetAnimValKey(), this, true);
278 //----------------------------------------------------------------------
279 // nsSVGElement methods
282 SVGPathElement::HasValidDimensions() const
284 return !mD
.GetAnimValue().IsEmpty();
287 nsSVGElement::NumberAttributesInfo
288 SVGPathElement::GetNumberInfo()
290 return NumberAttributesInfo(&mPathLength
, &sNumberInfo
, 1);
293 //----------------------------------------------------------------------
294 // nsIContent methods
297 SVGPathElement::IsAttributeMapped(const nsIAtom
* name
) const
299 static const MappedAttributeEntry
* const map
[] = {
303 return FindAttributeDependence(name
, map
) ||
304 SVGPathElementBase::IsAttributeMapped(name
);
308 SVGPathElement::GetOrBuildPathForMeasuring()
310 return mD
.GetAnimValue().BuildPathForMeasuring();
313 //----------------------------------------------------------------------
314 // nsSVGPathGeometryElement methods
317 SVGPathElement::AttributeDefinesGeometry(const nsIAtom
*aName
)
319 return aName
== nsGkAtoms::d
||
320 aName
== nsGkAtoms::pathLength
;
324 SVGPathElement::IsMarkable()
330 SVGPathElement::GetMarkPoints(nsTArray
<nsSVGMark
> *aMarks
)
332 mD
.GetAnimValue().GetMarkerPositioningData(aMarks
);
336 SVGPathElement::GetPathLengthScale(PathLengthScaleForType aFor
)
338 NS_ABORT_IF_FALSE(aFor
== eForTextPath
|| aFor
== eForStroking
,
340 if (mPathLength
.IsExplicitlySet()) {
341 float authorsPathLengthEstimate
= mPathLength
.GetAnimValue();
342 if (authorsPathLengthEstimate
> 0) {
343 RefPtr
<Path
> path
= GetOrBuildPathForMeasuring();
345 // The path is empty or invalid so its length must be zero and
346 // we know that 0 / authorsPathLengthEstimate = 0.
349 if (aFor
== eForTextPath
) {
350 // For textPath, a transform on the referenced path affects the
351 // textPath layout, so when calculating the actual path length
352 // we need to take that into account.
353 gfxMatrix matrix
= PrependLocalTransformsTo(gfxMatrix());
354 if (!matrix
.IsIdentity()) {
355 RefPtr
<PathBuilder
> builder
=
356 path
->TransformedCopyToBuilder(ToMatrix(matrix
));
357 path
= builder
->Finish();
360 return path
->ComputeLength() / authorsPathLengthEstimate
;
367 SVGPathElement::BuildPath(PathBuilder
* aBuilder
)
369 // The Moz2D PathBuilder that our SVGPathData will be using only cares about
370 // the fill rule. However, in order to fulfill the requirements of the SVG
371 // spec regarding zero length sub-paths when square line caps are in use,
372 // SVGPathData needs to know our stroke-linecap style and, if "square", then
373 // also our stroke width. See the comment for
374 // ApproximateZeroLengthSubpathSquareCaps for more info.
376 uint8_t strokeLineCap
= NS_STYLE_STROKE_LINECAP_BUTT
;
377 Float strokeWidth
= 0;
379 nsRefPtr
<nsStyleContext
> styleContext
=
380 nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr, nullptr);
382 const nsStyleSVG
* style
= styleContext
->StyleSVG();
383 // Note: the path that we return may be used for hit-testing, and SVG
384 // exposes hit-testing of strokes that are not actually painted. For that
385 // reason we do not check for eStyleSVGPaintType_None or check the stroke
387 if (style
->mStrokeLinecap
!= NS_STYLE_STROKE_LINECAP_BUTT
) {
388 strokeLineCap
= style
->mStrokeLinecap
;
389 strokeWidth
= SVGContentUtils::GetStrokeWidth(this, styleContext
, nullptr);
393 return mD
.GetAnimValue().BuildPath(aBuilder
, strokeLineCap
, strokeWidth
);
397 } // namespace mozilla