Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / dom / svg / SVGPathSegUtils.h
blobf0877d9a59cc8163b8ada29aeb14faae9272b131
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/. */
7 #ifndef DOM_SVG_SVGPATHSEGUTILS_H_
8 #define DOM_SVG_SVGPATHSEGUTILS_H_
10 #include "mozilla/ArrayUtils.h"
11 #include "mozilla/dom/SVGPathSegBinding.h"
12 #include "mozilla/gfx/Point.h"
13 #include "mozilla/gfx/Rect.h"
14 #include "nsDebug.h"
16 namespace mozilla {
18 struct StylePathCommand;
20 #define NS_SVG_PATH_SEG_MAX_ARGS 7
21 #define NS_SVG_PATH_SEG_FIRST_VALID_TYPE \
22 dom::SVGPathSeg_Binding::PATHSEG_CLOSEPATH
23 #define NS_SVG_PATH_SEG_LAST_VALID_TYPE \
24 dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
25 #define NS_SVG_PATH_SEG_TYPE_COUNT (NS_SVG_PATH_SEG_LAST_VALID_TYPE + 1)
27 /**
28 * Code that works with path segments can use an instance of this class to
29 * store/provide information about the start of the current subpath and the
30 * last path segment (if any).
32 struct SVGPathTraversalState {
33 using Point = gfx::Point;
35 enum TraversalMode { eUpdateAll, eUpdateOnlyStartAndCurrentPos };
37 SVGPathTraversalState()
38 : start(0.0, 0.0),
39 pos(0.0, 0.0),
40 cp1(0.0, 0.0),
41 cp2(0.0, 0.0),
42 length(0.0),
43 mode(eUpdateAll) {}
45 bool ShouldUpdateLengthAndControlPoints() { return mode == eUpdateAll; }
47 Point start; // start point of current sub path (reset each moveto)
49 Point pos; // current position (end point of previous segment)
51 Point cp1; // quadratic control point - if the previous segment was a
52 // quadratic bezier curve then this is set to the absolute
53 // position of its control point, otherwise its set to pos
55 Point cp2; // cubic control point - if the previous segment was a cubic
56 // bezier curve then this is set to the absolute position of
57 // its second control point, otherwise it's set to pos
59 float length; // accumulated path length
61 TraversalMode mode; // indicates what to track while traversing a path
64 /**
65 * This class is just a collection of static methods - it doesn't have any data
66 * members, and it's not possible to create instances of this class. This class
67 * exists purely as a convenient place to gather together a bunch of methods
68 * related to manipulating and answering questions about path segments.
69 * Internally we represent path segments purely as an array of floats. See the
70 * comment documenting SVGPathData for more info on that.
72 * The DOM wrapper classes for encoded path segments (data contained in
73 * instances of SVGPathData) is DOMSVGPathSeg and its sub-classes. Note that
74 * there are multiple different DOM classes for path segs - one for each of the
75 * 19 SVG 1.1 segment types.
77 class SVGPathSegUtils {
78 private:
79 SVGPathSegUtils() = default; // private to prevent instances
81 public:
82 static void GetValueAsString(const float* aSeg, nsAString& aValue);
84 /**
85 * Encode a segment type enum to a float.
87 * At some point in the future we will likely want to encode other
88 * information into the float, such as whether the command was explicit or
89 * not. For now all this method does is save on int to float runtime
90 * conversion by requiring uint32_t and float to be of the same size so we
91 * can simply do a bitwise uint32_t<->float copy.
93 static float EncodeType(uint32_t aType) {
94 static_assert(sizeof(uint32_t) == sizeof(float),
95 "sizeof uint32_t and float must be the same");
96 MOZ_ASSERT(IsValidType(aType), "Seg type not recognized");
97 return *(reinterpret_cast<float*>(&aType));
100 static uint32_t DecodeType(float aType) {
101 static_assert(sizeof(uint32_t) == sizeof(float),
102 "sizeof uint32_t and float must be the same");
103 uint32_t type = *(reinterpret_cast<uint32_t*>(&aType));
104 MOZ_ASSERT(IsValidType(type), "Seg type not recognized");
105 return type;
108 static char16_t GetPathSegTypeAsLetter(uint32_t aType) {
109 MOZ_ASSERT(IsValidType(aType), "Seg type not recognized");
111 static const char16_t table[] = {
112 char16_t('x'), // 0 == PATHSEG_UNKNOWN
113 char16_t('z'), // 1 == PATHSEG_CLOSEPATH
114 char16_t('M'), // 2 == PATHSEG_MOVETO_ABS
115 char16_t('m'), // 3 == PATHSEG_MOVETO_REL
116 char16_t('L'), // 4 == PATHSEG_LINETO_ABS
117 char16_t('l'), // 5 == PATHSEG_LINETO_REL
118 char16_t('C'), // 6 == PATHSEG_CURVETO_CUBIC_ABS
119 char16_t('c'), // 7 == PATHSEG_CURVETO_CUBIC_REL
120 char16_t('Q'), // 8 == PATHSEG_CURVETO_QUADRATIC_ABS
121 char16_t('q'), // 9 == PATHSEG_CURVETO_QUADRATIC_REL
122 char16_t('A'), // 10 == PATHSEG_ARC_ABS
123 char16_t('a'), // 11 == PATHSEG_ARC_REL
124 char16_t('H'), // 12 == PATHSEG_LINETO_HORIZONTAL_ABS
125 char16_t('h'), // 13 == PATHSEG_LINETO_HORIZONTAL_REL
126 char16_t('V'), // 14 == PATHSEG_LINETO_VERTICAL_ABS
127 char16_t('v'), // 15 == PATHSEG_LINETO_VERTICAL_REL
128 char16_t('S'), // 16 == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS
129 char16_t('s'), // 17 == PATHSEG_CURVETO_CUBIC_SMOOTH_REL
130 char16_t('T'), // 18 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS
131 char16_t('t') // 19 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
133 static_assert(MOZ_ARRAY_LENGTH(table) == NS_SVG_PATH_SEG_TYPE_COUNT,
134 "Unexpected table size");
136 return table[aType];
139 static uint32_t ArgCountForType(uint32_t aType) {
140 MOZ_ASSERT(IsValidType(aType), "Seg type not recognized");
142 static const uint8_t table[] = {
143 0, // 0 == PATHSEG_UNKNOWN
144 0, // 1 == PATHSEG_CLOSEPATH
145 2, // 2 == PATHSEG_MOVETO_ABS
146 2, // 3 == PATHSEG_MOVETO_REL
147 2, // 4 == PATHSEG_LINETO_ABS
148 2, // 5 == PATHSEG_LINETO_REL
149 6, // 6 == PATHSEG_CURVETO_CUBIC_ABS
150 6, // 7 == PATHSEG_CURVETO_CUBIC_REL
151 4, // 8 == PATHSEG_CURVETO_QUADRATIC_ABS
152 4, // 9 == PATHSEG_CURVETO_QUADRATIC_REL
153 7, // 10 == PATHSEG_ARC_ABS
154 7, // 11 == PATHSEG_ARC_REL
155 1, // 12 == PATHSEG_LINETO_HORIZONTAL_ABS
156 1, // 13 == PATHSEG_LINETO_HORIZONTAL_REL
157 1, // 14 == PATHSEG_LINETO_VERTICAL_ABS
158 1, // 15 == PATHSEG_LINETO_VERTICAL_REL
159 4, // 16 == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS
160 4, // 17 == PATHSEG_CURVETO_CUBIC_SMOOTH_REL
161 2, // 18 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS
162 2 // 19 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
164 static_assert(MOZ_ARRAY_LENGTH(table) == NS_SVG_PATH_SEG_TYPE_COUNT,
165 "Unexpected table size");
167 return table[aType];
171 * Convenience so that callers can pass a float containing an encoded type
172 * and have it decoded implicitly.
174 static uint32_t ArgCountForType(float aType) {
175 return ArgCountForType(DecodeType(aType));
178 static bool IsValidType(uint32_t aType) {
179 return aType >= NS_SVG_PATH_SEG_FIRST_VALID_TYPE &&
180 aType <= NS_SVG_PATH_SEG_LAST_VALID_TYPE;
183 static bool IsCubicType(uint32_t aType) {
184 return aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_CUBIC_REL ||
185 aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_CUBIC_ABS ||
186 aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_CUBIC_SMOOTH_REL ||
187 aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;
190 static bool IsQuadraticType(uint32_t aType) {
191 return aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_REL ||
192 aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_ABS ||
193 aType ==
194 dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL ||
195 aType ==
196 dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;
199 static bool IsArcType(uint32_t aType) {
200 return aType == dom::SVGPathSeg_Binding::PATHSEG_ARC_ABS ||
201 aType == dom::SVGPathSeg_Binding::PATHSEG_ARC_REL;
204 static bool IsRelativeOrAbsoluteType(uint32_t aType) {
205 MOZ_ASSERT(IsValidType(aType), "Seg type not recognized");
207 // When adding a new path segment type, ensure that the returned condition
208 // below is still correct.
209 static_assert(
210 NS_SVG_PATH_SEG_LAST_VALID_TYPE ==
211 dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL,
212 "Unexpected type");
214 return aType >= dom::SVGPathSeg_Binding::PATHSEG_MOVETO_ABS;
217 static bool IsRelativeType(uint32_t aType) {
218 MOZ_ASSERT(IsRelativeOrAbsoluteType(aType),
219 "IsRelativeType called with segment type that does not come in "
220 "relative and absolute forms");
222 // When adding a new path segment type, ensure that the returned condition
223 // below is still correct.
224 static_assert(
225 NS_SVG_PATH_SEG_LAST_VALID_TYPE ==
226 dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL,
227 "Unexpected type");
229 return aType & 1;
232 static uint32_t RelativeVersionOfType(uint32_t aType) {
233 MOZ_ASSERT(IsRelativeOrAbsoluteType(aType),
234 "RelativeVersionOfType called with segment type that does not "
235 "come in relative and absolute forms");
237 // When adding a new path segment type, ensure that the returned condition
238 // below is still correct.
239 static_assert(
240 NS_SVG_PATH_SEG_LAST_VALID_TYPE ==
241 dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL,
242 "Unexpected type");
244 return aType | 1;
247 static bool SameTypeModuloRelativeness(uint32_t aType1, uint32_t aType2) {
248 if (!IsRelativeOrAbsoluteType(aType1) ||
249 !IsRelativeOrAbsoluteType(aType2)) {
250 return aType1 == aType2;
253 return RelativeVersionOfType(aType1) == RelativeVersionOfType(aType2);
257 * Traverse the given path segment and update the SVGPathTraversalState
258 * object.
260 static void TraversePathSegment(const float* aData,
261 SVGPathTraversalState& aState);
264 * Traverse the given path segment and update the SVGPathTraversalState
265 * object. This is identical to the above one but accepts StylePathCommand.
267 static void TraversePathSegment(const StylePathCommand& aCommand,
268 SVGPathTraversalState& aState);
271 /// Detect whether the path represents a rectangle (for both filling AND
272 /// stroking) and if so returns it.
274 /// This is typically useful for google slides which has many of these rectangle
275 /// shaped paths. It handles the same scenarios as skia's
276 /// SkPathPriv::IsRectContour which it is inspried from, including zero-length
277 /// edges and multiple points on edges of the rectangle, and doesn't attempt to
278 /// detect flat curves (that could easily be added but the expectation is that
279 /// since skia doesn't fast path it we're not likely to run into it in
280 /// practice).
282 /// We could implement something similar for polygons.
283 Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath);
285 } // namespace mozilla
287 #endif // DOM_SVG_SVGPATHSEGUTILS_H_