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/. */
6 
7 #ifndef DOM_SVG_SVGPATHSEGUTILS_H_
8 #define DOM_SVG_SVGPATHSEGUTILS_H_
9 
10 #include "mozilla/ArrayUtils.h"
11 #include "mozilla/dom/SVGPathSegBinding.h"
12 #include "mozilla/gfx/Point.h"
13 #include "nsDebug.h"
14 
15 namespace mozilla {
16 
17 struct StylePathCommand;
18 
19 #define NS_SVG_PATH_SEG_MAX_ARGS 7
20 #define NS_SVG_PATH_SEG_FIRST_VALID_TYPE \
21   dom::SVGPathSeg_Binding::PATHSEG_CLOSEPATH
22 #define NS_SVG_PATH_SEG_LAST_VALID_TYPE \
23   dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
24 #define NS_SVG_PATH_SEG_TYPE_COUNT (NS_SVG_PATH_SEG_LAST_VALID_TYPE + 1)
25 
26 /**
27  * Code that works with path segments can use an instance of this class to
28  * store/provide information about the start of the current subpath and the
29  * last path segment (if any).
30  */
31 struct SVGPathTraversalState {
32   using Point = gfx::Point;
33 
34   enum TraversalMode { eUpdateAll, eUpdateOnlyStartAndCurrentPos };
35 
SVGPathTraversalStateSVGPathTraversalState36   SVGPathTraversalState()
37       : start(0.0, 0.0),
38         pos(0.0, 0.0),
39         cp1(0.0, 0.0),
40         cp2(0.0, 0.0),
41         length(0.0),
42         mode(eUpdateAll) {}
43 
ShouldUpdateLengthAndControlPointsSVGPathTraversalState44   bool ShouldUpdateLengthAndControlPoints() { return mode == eUpdateAll; }
45 
46   Point start;  // start point of current sub path (reset each moveto)
47 
48   Point pos;  // current position (end point of previous segment)
49 
50   Point cp1;  // quadratic control point - if the previous segment was a
51               // quadratic bezier curve then this is set to the absolute
52               // position of its control point, otherwise its set to pos
53 
54   Point cp2;  // cubic control point - if the previous segment was a cubic
55               // bezier curve then this is set to the absolute position of
56               // its second control point, otherwise it's set to pos
57 
58   float length;  // accumulated path length
59 
60   TraversalMode mode;  // indicates what to track while traversing a path
61 };
62 
63 /**
64  * This class is just a collection of static methods - it doesn't have any data
65  * members, and it's not possible to create instances of this class. This class
66  * exists purely as a convenient place to gather together a bunch of methods
67  * related to manipulating and answering questions about path segments.
68  * Internally we represent path segments purely as an array of floats. See the
69  * comment documenting SVGPathData for more info on that.
70  *
71  * The DOM wrapper classes for encoded path segments (data contained in
72  * instances of SVGPathData) is DOMSVGPathSeg and its sub-classes. Note that
73  * there are multiple different DOM classes for path segs - one for each of the
74  * 19 SVG 1.1 segment types.
75  */
76 class SVGPathSegUtils {
77  private:
78   SVGPathSegUtils() = default;  // private to prevent instances
79 
80  public:
81   static void GetValueAsString(const float* aSeg, nsAString& aValue);
82 
83   /**
84    * Encode a segment type enum to a float.
85    *
86    * At some point in the future we will likely want to encode other
87    * information into the float, such as whether the command was explicit or
88    * not. For now all this method does is save on int to float runtime
89    * conversion by requiring uint32_t and float to be of the same size so we
90    * can simply do a bitwise uint32_t<->float copy.
91    */
EncodeType(uint32_t aType)92   static float EncodeType(uint32_t aType) {
93     static_assert(sizeof(uint32_t) == sizeof(float),
94                   "sizeof uint32_t and float must be the same");
95     MOZ_ASSERT(IsValidType(aType), "Seg type not recognized");
96     return *(reinterpret_cast<float*>(&aType));
97   }
98 
DecodeType(float aType)99   static uint32_t DecodeType(float aType) {
100     static_assert(sizeof(uint32_t) == sizeof(float),
101                   "sizeof uint32_t and float must be the same");
102     uint32_t type = *(reinterpret_cast<uint32_t*>(&aType));
103     MOZ_ASSERT(IsValidType(type), "Seg type not recognized");
104     return type;
105   }
106 
GetPathSegTypeAsLetter(uint32_t aType)107   static char16_t GetPathSegTypeAsLetter(uint32_t aType) {
108     MOZ_ASSERT(IsValidType(aType), "Seg type not recognized");
109 
110     static const char16_t table[] = {
111         char16_t('x'),  //  0 == PATHSEG_UNKNOWN
112         char16_t('z'),  //  1 == PATHSEG_CLOSEPATH
113         char16_t('M'),  //  2 == PATHSEG_MOVETO_ABS
114         char16_t('m'),  //  3 == PATHSEG_MOVETO_REL
115         char16_t('L'),  //  4 == PATHSEG_LINETO_ABS
116         char16_t('l'),  //  5 == PATHSEG_LINETO_REL
117         char16_t('C'),  //  6 == PATHSEG_CURVETO_CUBIC_ABS
118         char16_t('c'),  //  7 == PATHSEG_CURVETO_CUBIC_REL
119         char16_t('Q'),  //  8 == PATHSEG_CURVETO_QUADRATIC_ABS
120         char16_t('q'),  //  9 == PATHSEG_CURVETO_QUADRATIC_REL
121         char16_t('A'),  // 10 == PATHSEG_ARC_ABS
122         char16_t('a'),  // 11 == PATHSEG_ARC_REL
123         char16_t('H'),  // 12 == PATHSEG_LINETO_HORIZONTAL_ABS
124         char16_t('h'),  // 13 == PATHSEG_LINETO_HORIZONTAL_REL
125         char16_t('V'),  // 14 == PATHSEG_LINETO_VERTICAL_ABS
126         char16_t('v'),  // 15 == PATHSEG_LINETO_VERTICAL_REL
127         char16_t('S'),  // 16 == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS
128         char16_t('s'),  // 17 == PATHSEG_CURVETO_CUBIC_SMOOTH_REL
129         char16_t('T'),  // 18 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS
130         char16_t('t')   // 19 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
131     };
132     static_assert(MOZ_ARRAY_LENGTH(table) == NS_SVG_PATH_SEG_TYPE_COUNT,
133                   "Unexpected table size");
134 
135     return table[aType];
136   }
137 
ArgCountForType(uint32_t aType)138   static uint32_t ArgCountForType(uint32_t aType) {
139     MOZ_ASSERT(IsValidType(aType), "Seg type not recognized");
140 
141     static const uint8_t table[] = {
142         0,  //  0 == PATHSEG_UNKNOWN
143         0,  //  1 == PATHSEG_CLOSEPATH
144         2,  //  2 == PATHSEG_MOVETO_ABS
145         2,  //  3 == PATHSEG_MOVETO_REL
146         2,  //  4 == PATHSEG_LINETO_ABS
147         2,  //  5 == PATHSEG_LINETO_REL
148         6,  //  6 == PATHSEG_CURVETO_CUBIC_ABS
149         6,  //  7 == PATHSEG_CURVETO_CUBIC_REL
150         4,  //  8 == PATHSEG_CURVETO_QUADRATIC_ABS
151         4,  //  9 == PATHSEG_CURVETO_QUADRATIC_REL
152         7,  // 10 == PATHSEG_ARC_ABS
153         7,  // 11 == PATHSEG_ARC_REL
154         1,  // 12 == PATHSEG_LINETO_HORIZONTAL_ABS
155         1,  // 13 == PATHSEG_LINETO_HORIZONTAL_REL
156         1,  // 14 == PATHSEG_LINETO_VERTICAL_ABS
157         1,  // 15 == PATHSEG_LINETO_VERTICAL_REL
158         4,  // 16 == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS
159         4,  // 17 == PATHSEG_CURVETO_CUBIC_SMOOTH_REL
160         2,  // 18 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS
161         2   // 19 == PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL
162     };
163     static_assert(MOZ_ARRAY_LENGTH(table) == NS_SVG_PATH_SEG_TYPE_COUNT,
164                   "Unexpected table size");
165 
166     return table[aType];
167   }
168 
169   /**
170    * Convenience so that callers can pass a float containing an encoded type
171    * and have it decoded implicitly.
172    */
ArgCountForType(float aType)173   static uint32_t ArgCountForType(float aType) {
174     return ArgCountForType(DecodeType(aType));
175   }
176 
IsValidType(uint32_t aType)177   static bool IsValidType(uint32_t aType) {
178     return aType >= NS_SVG_PATH_SEG_FIRST_VALID_TYPE &&
179            aType <= NS_SVG_PATH_SEG_LAST_VALID_TYPE;
180   }
181 
IsCubicType(uint32_t aType)182   static bool IsCubicType(uint32_t aType) {
183     return aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_CUBIC_REL ||
184            aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_CUBIC_ABS ||
185            aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_CUBIC_SMOOTH_REL ||
186            aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS;
187   }
188 
IsQuadraticType(uint32_t aType)189   static bool IsQuadraticType(uint32_t aType) {
190     return aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_REL ||
191            aType == dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_ABS ||
192            aType ==
193                dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL ||
194            aType ==
195                dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS;
196   }
197 
IsArcType(uint32_t aType)198   static bool IsArcType(uint32_t aType) {
199     return aType == dom::SVGPathSeg_Binding::PATHSEG_ARC_ABS ||
200            aType == dom::SVGPathSeg_Binding::PATHSEG_ARC_REL;
201   }
202 
IsRelativeOrAbsoluteType(uint32_t aType)203   static bool IsRelativeOrAbsoluteType(uint32_t aType) {
204     MOZ_ASSERT(IsValidType(aType), "Seg type not recognized");
205 
206     // When adding a new path segment type, ensure that the returned condition
207     // below is still correct.
208     static_assert(
209         NS_SVG_PATH_SEG_LAST_VALID_TYPE ==
210             dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL,
211         "Unexpected type");
212 
213     return aType >= dom::SVGPathSeg_Binding::PATHSEG_MOVETO_ABS;
214   }
215 
IsRelativeType(uint32_t aType)216   static bool IsRelativeType(uint32_t aType) {
217     MOZ_ASSERT(IsRelativeOrAbsoluteType(aType),
218                "IsRelativeType called with segment type that does not come in "
219                "relative and absolute forms");
220 
221     // When adding a new path segment type, ensure that the returned condition
222     // below is still correct.
223     static_assert(
224         NS_SVG_PATH_SEG_LAST_VALID_TYPE ==
225             dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL,
226         "Unexpected type");
227 
228     return aType & 1;
229   }
230 
RelativeVersionOfType(uint32_t aType)231   static uint32_t RelativeVersionOfType(uint32_t aType) {
232     MOZ_ASSERT(IsRelativeOrAbsoluteType(aType),
233                "RelativeVersionOfType called with segment type that does not "
234                "come in relative and absolute forms");
235 
236     // When adding a new path segment type, ensure that the returned condition
237     // below is still correct.
238     static_assert(
239         NS_SVG_PATH_SEG_LAST_VALID_TYPE ==
240             dom::SVGPathSeg_Binding::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL,
241         "Unexpected type");
242 
243     return aType | 1;
244   }
245 
SameTypeModuloRelativeness(uint32_t aType1,uint32_t aType2)246   static uint32_t SameTypeModuloRelativeness(uint32_t aType1, uint32_t aType2) {
247     if (!IsRelativeOrAbsoluteType(aType1)) {
248       return aType1 == aType2;
249     }
250 
251     return RelativeVersionOfType(aType1) == RelativeVersionOfType(aType2);
252   }
253 
254   /**
255    * Traverse the given path segment and update the SVGPathTraversalState
256    * object.
257    */
258   static void TraversePathSegment(const float* aData,
259                                   SVGPathTraversalState& aState);
260 
261   /**
262    * Traverse the given path segment and update the SVGPathTraversalState
263    * object. This is identical to the above one but accepts StylePathCommand.
264    */
265   static void TraversePathSegment(const StylePathCommand& aCommand,
266                                   SVGPathTraversalState& aState);
267 };
268 
269 }  // namespace mozilla
270 
271 #endif  // DOM_SVG_SVGPATHSEGUTILS_H_
272