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