summaryrefslogtreecommitdiffstats
path: root/dom/svg/SVGPathElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/svg/SVGPathElement.cpp')
-rw-r--r--dom/svg/SVGPathElement.cpp398
1 files changed, 398 insertions, 0 deletions
diff --git a/dom/svg/SVGPathElement.cpp b/dom/svg/SVGPathElement.cpp
new file mode 100644
index 000000000..85fce0072
--- /dev/null
+++ b/dom/svg/SVGPathElement.cpp
@@ -0,0 +1,398 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/SVGPathElement.h"
+
+#include <algorithm>
+
+#include "DOMSVGPathSeg.h"
+#include "DOMSVGPathSegList.h"
+#include "DOMSVGPoint.h"
+#include "gfx2DGlue.h"
+#include "gfxPlatform.h"
+#include "mozilla/dom/SVGPathElementBinding.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "nsComputedDOMStyle.h"
+#include "nsGkAtoms.h"
+#include "nsStyleConsts.h"
+#include "nsStyleStruct.h"
+#include "SVGContentUtils.h"
+
+NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Path)
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace dom {
+
+JSObject*
+SVGPathElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return SVGPathElementBinding::Wrap(aCx, this, aGivenProto);
+}
+
+nsSVGElement::NumberInfo SVGPathElement::sNumberInfo =
+{ &nsGkAtoms::pathLength, 0, false };
+
+//----------------------------------------------------------------------
+// Implementation
+
+SVGPathElement::SVGPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+ : SVGPathElementBase(aNodeInfo)
+{
+}
+
+//----------------------------------------------------------------------
+// memory reporting methods
+
+size_t
+SVGPathElement::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
+{
+ return SVGPathElementBase::SizeOfExcludingThis(aMallocSizeOf) +
+ mD.SizeOfExcludingThis(aMallocSizeOf);
+}
+
+//----------------------------------------------------------------------
+// nsIDOMNode methods
+
+NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGPathElement)
+
+already_AddRefed<SVGAnimatedNumber>
+SVGPathElement::PathLength()
+{
+ return mPathLength.ToDOMAnimatedNumber(this);
+}
+
+float
+SVGPathElement::GetTotalLength()
+{
+ RefPtr<Path> flat = GetOrBuildPathForMeasuring();
+ return flat ? flat->ComputeLength() : 0.f;
+}
+
+already_AddRefed<nsISVGPoint>
+SVGPathElement::GetPointAtLength(float distance, ErrorResult& rv)
+{
+ RefPtr<Path> path = GetOrBuildPathForMeasuring();
+ if (!path) {
+ rv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ float totalLength = path->ComputeLength();
+ if (mPathLength.IsExplicitlySet()) {
+ float pathLength = mPathLength.GetAnimValue();
+ if (pathLength <= 0) {
+ rv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+ distance *= totalLength / pathLength;
+ }
+ distance = std::max(0.f, distance);
+ distance = std::min(totalLength, distance);
+
+ nsCOMPtr<nsISVGPoint> point =
+ new DOMSVGPoint(path->ComputePointAtLength(distance));
+ return point.forget();
+}
+
+uint32_t
+SVGPathElement::GetPathSegAtLength(float distance)
+{
+ return mD.GetAnimValue().GetPathSegAtLength(distance);
+}
+
+already_AddRefed<DOMSVGPathSegClosePath>
+SVGPathElement::CreateSVGPathSegClosePath()
+{
+ RefPtr<DOMSVGPathSegClosePath> pathSeg = new DOMSVGPathSegClosePath();
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegMovetoAbs>
+SVGPathElement::CreateSVGPathSegMovetoAbs(float x, float y)
+{
+ RefPtr<DOMSVGPathSegMovetoAbs> pathSeg = new DOMSVGPathSegMovetoAbs(x, y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegMovetoRel>
+SVGPathElement::CreateSVGPathSegMovetoRel(float x, float y)
+{
+ RefPtr<DOMSVGPathSegMovetoRel> pathSeg = new DOMSVGPathSegMovetoRel(x, y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegLinetoAbs>
+SVGPathElement::CreateSVGPathSegLinetoAbs(float x, float y)
+{
+ RefPtr<DOMSVGPathSegLinetoAbs> pathSeg = new DOMSVGPathSegLinetoAbs(x, y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegLinetoRel>
+SVGPathElement::CreateSVGPathSegLinetoRel(float x, float y)
+{
+ RefPtr<DOMSVGPathSegLinetoRel> pathSeg = new DOMSVGPathSegLinetoRel(x, y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegCurvetoCubicAbs>
+SVGPathElement::CreateSVGPathSegCurvetoCubicAbs(float x, float y, float x1, float y1, float x2, float y2)
+{
+ // Note that we swap from DOM API argument order to the argument order used
+ // in the <path> element's 'd' attribute (i.e. we put the arguments for the
+ // end point of the segment last instead of first).
+ RefPtr<DOMSVGPathSegCurvetoCubicAbs> pathSeg =
+ new DOMSVGPathSegCurvetoCubicAbs(x1, y1, x2, y2, x, y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegCurvetoCubicRel>
+SVGPathElement::CreateSVGPathSegCurvetoCubicRel(float x, float y, float x1, float y1, float x2, float y2)
+{
+ // See comment in CreateSVGPathSegCurvetoCubicAbs
+ RefPtr<DOMSVGPathSegCurvetoCubicRel> pathSeg =
+ new DOMSVGPathSegCurvetoCubicRel(x1, y1, x2, y2, x, y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegCurvetoQuadraticAbs>
+SVGPathElement::CreateSVGPathSegCurvetoQuadraticAbs(float x, float y, float x1, float y1)
+{
+ // See comment in CreateSVGPathSegCurvetoCubicAbs
+ RefPtr<DOMSVGPathSegCurvetoQuadraticAbs> pathSeg =
+ new DOMSVGPathSegCurvetoQuadraticAbs(x1, y1, x, y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegCurvetoQuadraticRel>
+SVGPathElement::CreateSVGPathSegCurvetoQuadraticRel(float x, float y, float x1, float y1)
+{
+ // See comment in CreateSVGPathSegCurvetoCubicAbs
+ RefPtr<DOMSVGPathSegCurvetoQuadraticRel> pathSeg =
+ new DOMSVGPathSegCurvetoQuadraticRel(x1, y1, x, y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegArcAbs>
+SVGPathElement::CreateSVGPathSegArcAbs(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag)
+{
+ // See comment in CreateSVGPathSegCurvetoCubicAbs
+ RefPtr<DOMSVGPathSegArcAbs> pathSeg =
+ new DOMSVGPathSegArcAbs(r1, r2, angle, largeArcFlag, sweepFlag, x, y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegArcRel>
+SVGPathElement::CreateSVGPathSegArcRel(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag)
+{
+ // See comment in CreateSVGPathSegCurvetoCubicAbs
+ RefPtr<DOMSVGPathSegArcRel> pathSeg =
+ new DOMSVGPathSegArcRel(r1, r2, angle, largeArcFlag, sweepFlag, x, y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegLinetoHorizontalAbs>
+SVGPathElement::CreateSVGPathSegLinetoHorizontalAbs(float x)
+{
+ RefPtr<DOMSVGPathSegLinetoHorizontalAbs> pathSeg =
+ new DOMSVGPathSegLinetoHorizontalAbs(x);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegLinetoHorizontalRel>
+SVGPathElement::CreateSVGPathSegLinetoHorizontalRel(float x)
+{
+ RefPtr<DOMSVGPathSegLinetoHorizontalRel> pathSeg =
+ new DOMSVGPathSegLinetoHorizontalRel(x);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegLinetoVerticalAbs>
+SVGPathElement::CreateSVGPathSegLinetoVerticalAbs(float y)
+{
+ RefPtr<DOMSVGPathSegLinetoVerticalAbs> pathSeg =
+ new DOMSVGPathSegLinetoVerticalAbs(y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegLinetoVerticalRel>
+SVGPathElement::CreateSVGPathSegLinetoVerticalRel(float y)
+{
+ RefPtr<DOMSVGPathSegLinetoVerticalRel> pathSeg =
+ new DOMSVGPathSegLinetoVerticalRel(y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegCurvetoCubicSmoothAbs>
+SVGPathElement::CreateSVGPathSegCurvetoCubicSmoothAbs(float x, float y, float x2, float y2)
+{
+ // See comment in CreateSVGPathSegCurvetoCubicAbs
+ RefPtr<DOMSVGPathSegCurvetoCubicSmoothAbs> pathSeg =
+ new DOMSVGPathSegCurvetoCubicSmoothAbs(x2, y2, x, y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegCurvetoCubicSmoothRel>
+SVGPathElement::CreateSVGPathSegCurvetoCubicSmoothRel(float x, float y, float x2, float y2)
+{
+ // See comment in CreateSVGPathSegCurvetoCubicAbs
+ RefPtr<DOMSVGPathSegCurvetoCubicSmoothRel> pathSeg =
+ new DOMSVGPathSegCurvetoCubicSmoothRel(x2, y2, x, y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegCurvetoQuadraticSmoothAbs>
+SVGPathElement::CreateSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y)
+{
+ RefPtr<DOMSVGPathSegCurvetoQuadraticSmoothAbs> pathSeg =
+ new DOMSVGPathSegCurvetoQuadraticSmoothAbs(x, y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegCurvetoQuadraticSmoothRel>
+SVGPathElement::CreateSVGPathSegCurvetoQuadraticSmoothRel(float x, float y)
+{
+ RefPtr<DOMSVGPathSegCurvetoQuadraticSmoothRel> pathSeg =
+ new DOMSVGPathSegCurvetoQuadraticSmoothRel(x, y);
+ return pathSeg.forget();
+}
+
+already_AddRefed<DOMSVGPathSegList>
+SVGPathElement::PathSegList()
+{
+ return DOMSVGPathSegList::GetDOMWrapper(mD.GetBaseValKey(), this, false);
+}
+
+already_AddRefed<DOMSVGPathSegList>
+SVGPathElement::AnimatedPathSegList()
+{
+ return DOMSVGPathSegList::GetDOMWrapper(mD.GetAnimValKey(), this, true);
+}
+
+//----------------------------------------------------------------------
+// nsSVGElement methods
+
+/* virtual */ bool
+SVGPathElement::HasValidDimensions() const
+{
+ return !mD.GetAnimValue().IsEmpty();
+}
+
+nsSVGElement::NumberAttributesInfo
+SVGPathElement::GetNumberInfo()
+{
+ return NumberAttributesInfo(&mPathLength, &sNumberInfo, 1);
+}
+
+//----------------------------------------------------------------------
+// nsIContent methods
+
+NS_IMETHODIMP_(bool)
+SVGPathElement::IsAttributeMapped(const nsIAtom* name) const
+{
+ static const MappedAttributeEntry* const map[] = {
+ sMarkersMap
+ };
+
+ return FindAttributeDependence(name, map) ||
+ SVGPathElementBase::IsAttributeMapped(name);
+}
+
+already_AddRefed<Path>
+SVGPathElement::GetOrBuildPathForMeasuring()
+{
+ return mD.GetAnimValue().BuildPathForMeasuring();
+}
+
+//----------------------------------------------------------------------
+// nsSVGPathGeometryElement methods
+
+bool
+SVGPathElement::AttributeDefinesGeometry(const nsIAtom *aName)
+{
+ return aName == nsGkAtoms::d ||
+ aName == nsGkAtoms::pathLength;
+}
+
+bool
+SVGPathElement::IsMarkable()
+{
+ return true;
+}
+
+void
+SVGPathElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks)
+{
+ mD.GetAnimValue().GetMarkerPositioningData(aMarks);
+}
+
+float
+SVGPathElement::GetPathLengthScale(PathLengthScaleForType aFor)
+{
+ MOZ_ASSERT(aFor == eForTextPath || aFor == eForStroking,
+ "Unknown enum");
+ if (mPathLength.IsExplicitlySet()) {
+ float authorsPathLengthEstimate = mPathLength.GetAnimValue();
+ if (authorsPathLengthEstimate > 0) {
+ RefPtr<Path> path = GetOrBuildPathForMeasuring();
+ if (!path) {
+ // The path is empty or invalid so its length must be zero and
+ // we know that 0 / authorsPathLengthEstimate = 0.
+ return 0.0;
+ }
+ if (aFor == eForTextPath) {
+ // For textPath, a transform on the referenced path affects the
+ // textPath layout, so when calculating the actual path length
+ // we need to take that into account.
+ gfxMatrix matrix = PrependLocalTransformsTo(gfxMatrix());
+ if (!matrix.IsIdentity()) {
+ RefPtr<PathBuilder> builder =
+ path->TransformedCopyToBuilder(ToMatrix(matrix));
+ path = builder->Finish();
+ }
+ }
+ return path->ComputeLength() / authorsPathLengthEstimate;
+ }
+ }
+ return 1.0;
+}
+
+already_AddRefed<Path>
+SVGPathElement::BuildPath(PathBuilder* aBuilder)
+{
+ // The Moz2D PathBuilder that our SVGPathData will be using only cares about
+ // the fill rule. However, in order to fulfill the requirements of the SVG
+ // spec regarding zero length sub-paths when square line caps are in use,
+ // SVGPathData needs to know our stroke-linecap style and, if "square", then
+ // also our stroke width. See the comment for
+ // ApproximateZeroLengthSubpathSquareCaps for more info.
+
+ uint8_t strokeLineCap = NS_STYLE_STROKE_LINECAP_BUTT;
+ Float strokeWidth = 0;
+
+ RefPtr<nsStyleContext> styleContext =
+ nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr, nullptr);
+ if (styleContext) {
+ const nsStyleSVG* style = styleContext->StyleSVG();
+ // Note: the path that we return may be used for hit-testing, and SVG
+ // exposes hit-testing of strokes that are not actually painted. For that
+ // reason we do not check for eStyleSVGPaintType_None or check the stroke
+ // opacity here.
+ if (style->mStrokeLinecap != NS_STYLE_STROKE_LINECAP_BUTT) {
+ strokeLineCap = style->mStrokeLinecap;
+ strokeWidth = SVGContentUtils::GetStrokeWidth(this, styleContext, nullptr);
+ }
+ }
+
+ return mD.GetAnimValue().BuildPath(aBuilder, strokeLineCap, strokeWidth);
+}
+
+} // namespace dom
+} // namespace mozilla