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