summaryrefslogtreecommitdiffstats
path: root/dom/svg/SVGPathData.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/svg/SVGPathData.h')
-rw-r--r--dom/svg/SVGPathData.h320
1 files changed, 320 insertions, 0 deletions
diff --git a/dom/svg/SVGPathData.h b/dom/svg/SVGPathData.h
new file mode 100644
index 0000000000..4aa60de3cb
--- /dev/null
+++ b/dom/svg/SVGPathData.h
@@ -0,0 +1,320 @@
+/* -*- 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/. */
+
+#ifndef DOM_SVG_SVGPATHDATA_H_
+#define DOM_SVG_SVGPATHDATA_H_
+
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+#include "nsIContent.h"
+#include "nsINode.h"
+#include "nsIWeakReferenceUtils.h"
+#include "mozilla/dom/SVGElement.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Types.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/RefPtr.h"
+#include "nsTArray.h"
+
+#include <string.h>
+
+namespace mozilla {
+
+struct StylePathCommand;
+struct SVGMark;
+enum class StyleStrokeLinecap : uint8_t;
+
+class SVGPathDataParser; // IWYU pragma: keep
+
+namespace dom {
+class DOMSVGPathSeg;
+class DOMSVGPathSegList;
+} // namespace dom
+
+/**
+ * ATTENTION! WARNING! WATCH OUT!!
+ *
+ * Consumers that modify objects of this type absolutely MUST keep the DOM
+ * wrappers for those lists (if any) in sync!! That's why this class is so
+ * locked down.
+ *
+ * The DOM wrapper class for this class is DOMSVGPathSegList.
+ *
+ * This class is not called |class SVGPathSegList| for one very good reason;
+ * this class does not provide a list of "SVGPathSeg" items, it provides an
+ * array of floats into which path segments are encoded. See the paragraphs
+ * that follow for why. Note that the Length() method returns the number of
+ * floats in our array, not the number of encoded segments, and the index
+ * operator indexes floats in the array, not segments. If this class were
+ * called SVGPathSegList the names of these methods would be very misleading.
+ *
+ * The reason this class is designed in this way is because there are many
+ * different types of path segment, each taking a different numbers of
+ * arguments. We want to store the segments in an nsTArray to avoid individual
+ * allocations for each item, but the different size of segments means we can't
+ * have one single segment type for the nsTArray (not without using a space
+ * wasteful union or something similar). Since the internal code does not need
+ * to index into the list (the DOM wrapper does, but it handles that itself)
+ * the obvious solution is to have the items in this class take up variable
+ * width and have the internal code iterate over these lists rather than index
+ * into them.
+ *
+ * Implementing indexing to segments with O(1) performance would require us to
+ * allocate and maintain a separate segment index table (keeping that table in
+ * sync when items are inserted or removed from the list). So long as the
+ * internal code doesn't require indexing to segments, we can avoid that
+ * overhead and additional complexity.
+ *
+ * Segment encoding: the first float in the encoding of a segment contains the
+ * segment's type. The segment's type is encoded to/decoded from this float
+ * using the static methods SVGPathSegUtils::EncodeType(uint32_t)/
+ * SVGPathSegUtils::DecodeType(float). If the path segment type in question
+ * takes any arguments then these follow the first float, and are in the same
+ * order as they are given in a <path> element's 'd' attribute (NOT in the
+ * order of the createSVGPathSegXxx() methods' arguments from the SVG DOM
+ * interface SVGPathElement, which are different...grr). Consumers can use
+ * SVGPathSegUtils::ArgCountForType(type) to determine how many arguments
+ * there are (if any), and thus where the current encoded segment ends, and
+ * where the next segment (if any) begins.
+ */
+class SVGPathData {
+ friend class SVGAnimatedPathSegList;
+ friend class dom::DOMSVGPathSeg;
+ friend class dom::DOMSVGPathSegList;
+ friend class SVGPathDataParser;
+ // SVGPathDataParser will not keep wrappers in sync, so consumers
+ // are responsible for that!
+
+ using DrawTarget = gfx::DrawTarget;
+ using Path = gfx::Path;
+ using PathBuilder = gfx::PathBuilder;
+ using FillRule = gfx::FillRule;
+ using Float = gfx::Float;
+ using CapStyle = gfx::CapStyle;
+
+ public:
+ using const_iterator = const float*;
+
+ SVGPathData() = default;
+ ~SVGPathData() = default;
+
+ SVGPathData& operator=(const SVGPathData& aOther) {
+ mData.ClearAndRetainStorage();
+ // Best-effort, really.
+ Unused << mData.AppendElements(aOther.mData, fallible);
+ return *this;
+ }
+
+ SVGPathData(const SVGPathData& aOther) { *this = aOther; }
+
+ // Only methods that don't make/permit modification to this list are public.
+ // Only our friend classes can access methods that may change us.
+
+ /// This may return an incomplete string on OOM, but that's acceptable.
+ void GetValueAsString(nsAString& aValue) const;
+
+ bool IsEmpty() const { return mData.IsEmpty(); }
+
+#ifdef DEBUG
+ /**
+ * This method iterates over the encoded segment data and counts the number
+ * of segments we currently have.
+ */
+ uint32_t CountItems() const;
+#endif
+
+ /**
+ * Returns the number of *floats* in the encoding array, and NOT the number
+ * of segments encoded in this object. (For that, see CountItems() above.)
+ */
+ uint32_t Length() const { return mData.Length(); }
+
+ const nsTArray<float>& RawData() const { return mData; }
+
+ const float& operator[](uint32_t aIndex) const { return mData[aIndex]; }
+
+ // Used by SMILCompositor to check if the cached base val is out of date
+ bool operator==(const SVGPathData& rhs) const {
+ // We use memcmp so that we don't need to worry that the data encoded in
+ // the first float may have the same bit pattern as a NaN.
+ return mData.Length() == rhs.mData.Length() &&
+ memcmp(mData.Elements(), rhs.mData.Elements(),
+ mData.Length() * sizeof(float)) == 0;
+ }
+
+ bool SetCapacity(uint32_t aSize) {
+ return mData.SetCapacity(aSize, fallible);
+ }
+
+ void Compact() { mData.Compact(); }
+
+ float GetPathLength() const;
+
+ uint32_t GetPathSegAtLength(float aDistance) const;
+
+ static uint32_t GetPathSegAtLength(Span<const StylePathCommand> aPath,
+ float aDistance);
+
+ void GetMarkerPositioningData(nsTArray<SVGMark>* aMarks) const;
+
+ static void GetMarkerPositioningData(Span<const StylePathCommand> aPath,
+ nsTArray<SVGMark>* aMarks);
+
+ /**
+ * Returns true, except on OOM, in which case returns false.
+ */
+ bool GetDistancesFromOriginToEndsOfVisibleSegments(
+ FallibleTArray<double>* aOutput) const;
+
+ /**
+ * This is identical to the above one but accepts StylePathCommand.
+ */
+ static bool GetDistancesFromOriginToEndsOfVisibleSegments(
+ Span<const StylePathCommand> aPath, FallibleTArray<double>* aOutput);
+
+ /**
+ * This returns a path without the extra little line segments that
+ * ApproximateZeroLengthSubpathSquareCaps can insert if we have square-caps.
+ * See the comment for that function for more info on that.
+ */
+ already_AddRefed<Path> BuildPathForMeasuring() const;
+
+ already_AddRefed<Path> BuildPath(PathBuilder* aBuilder,
+ StyleStrokeLinecap aStrokeLineCap,
+ Float aStrokeWidth) const;
+
+ static already_AddRefed<Path> BuildPathForMeasuring(
+ Span<const StylePathCommand> aPath);
+
+ /**
+ * This function tries to build the path from an array of StylePathCommand,
+ * which is generated by cbindgen from Rust (see ServoStyleConsts.h).
+ * Basically, this is a variant of the above BuildPath() functions.
+ */
+ static already_AddRefed<Path> BuildPath(
+ Span<const StylePathCommand> aPath, PathBuilder* aBuilder,
+ StyleStrokeLinecap aStrokeLineCap, Float aStrokeWidth,
+ const gfx::Point& aOffset = gfx::Point(), float aZoomFactor = 1.0);
+
+ const_iterator begin() const { return mData.Elements(); }
+ const_iterator end() const { return mData.Elements() + mData.Length(); }
+
+ // memory reporting methods
+ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
+ // Access to methods that can modify objects of this type is deliberately
+ // limited. This is to reduce the chances of someone modifying objects of
+ // this type without taking the necessary steps to keep DOM wrappers in sync.
+ // If you need wider access to these methods, consider adding a method to
+ // SVGAnimatedPathSegList and having that class act as an intermediary so it
+ // can take care of keeping DOM wrappers in sync.
+
+ protected:
+ using iterator = float*;
+
+ /**
+ * This may fail on OOM if the internal capacity needs to be increased, in
+ * which case the list will be left unmodified.
+ */
+ nsresult CopyFrom(const SVGPathData& rhs);
+ void SwapWith(SVGPathData& aRhs) { mData.SwapElements(aRhs.mData); }
+
+ float& operator[](uint32_t aIndex) { return mData[aIndex]; }
+
+ /**
+ * This may fail (return false) on OOM if the internal capacity is being
+ * increased, in which case the list will be left unmodified.
+ */
+ bool SetLength(uint32_t aLength) {
+ return mData.SetLength(aLength, fallible);
+ }
+
+ nsresult SetValueFromString(const nsAString& aValue);
+
+ void Clear() { mData.Clear(); }
+
+ // Our DOM wrappers have direct access to our mData, so they directly
+ // manipulate it rather than us implementing:
+ //
+ // * InsertItem(uint32_t aDataIndex, uint32_t aType, const float *aArgs);
+ // * ReplaceItem(uint32_t aDataIndex, uint32_t aType, const float *aArgs);
+ // * RemoveItem(uint32_t aDataIndex);
+ // * bool AppendItem(uint32_t aType, const float *aArgs);
+
+ nsresult AppendSeg(uint32_t aType, ...); // variable number of float args
+
+ iterator begin() { return mData.Elements(); }
+ iterator end() { return mData.Elements() + mData.Length(); }
+
+ FallibleTArray<float> mData;
+};
+
+/**
+ * This SVGPathData subclass is for SVGPathSegListSMILType which needs to
+ * have write access to the lists it works with.
+ *
+ * Instances of this class do not have DOM wrappers that need to be kept in
+ * sync, so we can safely expose any protected base class methods required by
+ * the SMIL code.
+ */
+class SVGPathDataAndInfo final : public SVGPathData {
+ public:
+ explicit SVGPathDataAndInfo(dom::SVGElement* aElement = nullptr)
+ : mElement(do_GetWeakReference(static_cast<nsINode*>(aElement))) {}
+
+ void SetElement(dom::SVGElement* aElement) {
+ mElement = do_GetWeakReference(static_cast<nsINode*>(aElement));
+ }
+
+ dom::SVGElement* Element() const {
+ nsCOMPtr<nsIContent> e = do_QueryReferent(mElement);
+ return static_cast<dom::SVGElement*>(e.get());
+ }
+
+ nsresult CopyFrom(const SVGPathDataAndInfo& rhs) {
+ mElement = rhs.mElement;
+ return SVGPathData::CopyFrom(rhs);
+ }
+
+ /**
+ * Returns true if this object is an "identity" value, from the perspective
+ * of SMIL. In other words, returns true until the initial value set up in
+ * SVGPathSegListSMILType::Init() has been changed with a SetElement() call.
+ */
+ bool IsIdentity() const {
+ if (!mElement) {
+ MOZ_ASSERT(IsEmpty(), "target element propagation failure");
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Exposed so that SVGPathData baseVals can be copied to
+ * SVGPathDataAndInfo objects. Note that callers should also call
+ * SetElement() when using this method!
+ */
+ using SVGPathData::CopyFrom;
+
+ // Exposed since SVGPathData objects can be modified.
+ using SVGPathData::iterator;
+ using SVGPathData::operator[];
+ using SVGPathData::begin;
+ using SVGPathData::end;
+ using SVGPathData::SetLength;
+
+ private:
+ // We must keep a weak reference to our element because we may belong to a
+ // cached baseVal SMILValue. See the comments starting at:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=515116#c15
+ // See also https://bugzilla.mozilla.org/show_bug.cgi?id=653497
+ nsWeakPtr mElement;
+};
+
+} // namespace mozilla
+
+#endif // DOM_SVG_SVGPATHDATA_H_