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