SVGContentUtils.h (11915B)
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_SVGCONTENTUTILS_H_ 8 #define DOM_SVG_SVGCONTENTUTILS_H_ 9 10 // include math.h to pick up definition of M_ maths defines e.g. M_PI 11 #include <math.h> 12 13 #include "gfx2DGlue.h" 14 #include "mozilla/EnumSet.h" 15 #include "mozilla/gfx/2D.h" // for StrokeOptions 16 #include "mozilla/gfx/Matrix.h" 17 #include "nsDependentSubstring.h" 18 #include "nsError.h" 19 #include "nsStringFwd.h" 20 #include "nsTArray.h" 21 22 class nsIContent; 23 24 class nsIFrame; 25 class nsPresContext; 26 27 namespace mozilla { 28 class ComputedStyle; 29 class SVGAnimatedTransformList; 30 class SVGAnimatedPreserveAspectRatio; 31 class SVGContextPaint; 32 class SVGPreserveAspectRatio; 33 union StyleLengthPercentageUnion; 34 namespace dom { 35 class Document; 36 class Element; 37 class SVGElement; 38 class SVGSVGElement; 39 class SVGViewportElement; 40 } // namespace dom 41 42 #define SVG_ZERO_LENGTH_PATH_FIX_FACTOR 512 43 44 /** 45 * Functions generally used by SVG Content classes. Functions here 46 * should not generally depend on layout methods/classes e.g. SVGUtils 47 */ 48 class SVGContentUtils { 49 public: 50 using Float = gfx::Float; 51 using Matrix = gfx::Matrix; 52 using Rect = gfx::Rect; 53 using StrokeOptions = gfx::StrokeOptions; 54 55 /* 56 * Get the outer SVG element of an nsIContent 57 */ 58 static dom::SVGSVGElement* GetOuterSVGElement(dom::SVGElement* aSVGElement); 59 60 /** 61 * Moz2D's StrokeOptions requires someone else to own its mDashPattern 62 * buffer, which is a pain when you want to initialize a StrokeOptions object 63 * in a helper function and pass it out. This sub-class owns the mDashPattern 64 * buffer so that consumers of such a helper function don't need to worry 65 * about creating it, passing it in, or deleting it. (An added benefit is 66 * that in the typical case when stroke-dasharray is short it will avoid 67 * allocating.) 68 */ 69 struct AutoStrokeOptions : public StrokeOptions { 70 AutoStrokeOptions() { 71 MOZ_ASSERT(mDashLength == 0, "InitDashPattern() depends on this"); 72 } 73 ~AutoStrokeOptions() { 74 if (mDashPattern && mDashPattern != mSmallArray) { 75 delete[] mDashPattern; 76 } 77 } 78 /** 79 * Creates the buffer to store the stroke-dasharray, assuming out-of-memory 80 * does not occur. The buffer's address is assigned to mDashPattern and 81 * returned to the caller as a non-const pointer (so that the caller can 82 * initialize the values in the buffer, since mDashPattern is const). 83 */ 84 Float* InitDashPattern(size_t aDashCount) { 85 if (aDashCount <= std::size(mSmallArray)) { 86 mDashPattern = mSmallArray; 87 return mSmallArray; 88 } 89 Float* nonConstArray = new (fallible) Float[aDashCount]; 90 mDashPattern = nonConstArray; 91 return nonConstArray; 92 } 93 void DiscardDashPattern() { 94 if (mDashPattern && mDashPattern != mSmallArray) { 95 delete[] mDashPattern; 96 } 97 mDashLength = 0; 98 mDashPattern = nullptr; 99 } 100 101 private: 102 // Most dasharrays will fit in this and save us allocating 103 Float mSmallArray[16]; 104 }; 105 106 enum class StrokeOptionFlag { IgnoreStrokeDashing }; 107 using StrokeOptionFlags = EnumSet<StrokeOptionFlag>; 108 109 /** 110 * Note: the linecap style returned in aStrokeOptions is not valid when 111 * ShapeTypeHasNoCorners(aElement) == true && aFlags == eIgnoreStrokeDashing, 112 * since when aElement has no corners the rendered linecap style depends on 113 * whether or not the stroke is dashed. 114 */ 115 static void GetStrokeOptions(AutoStrokeOptions* aStrokeOptions, 116 dom::SVGElement* aElement, 117 const ComputedStyle* aComputedStyle, 118 const SVGContextPaint* aContextPaint, 119 StrokeOptionFlags aFlags = {}); 120 121 /** 122 * Returns the current computed value of the CSS property 'stroke-width' for 123 * the given element. aComputedStyle may be provided as an optimization. 124 * aContextPaint is also optional. 125 * 126 * Note that this function does NOT take account of the value of the 'stroke' 127 * and 'stroke-opacity' properties to, say, return zero if they are "none" or 128 * "0", respectively. 129 */ 130 static Float GetStrokeWidth(const dom::SVGElement* aElement, 131 const ComputedStyle* aComputedStyle, 132 const SVGContextPaint* aContextPaint); 133 134 /* 135 * Get the number of CSS px (user units) per em (i.e. the em-height in user 136 * units) for an nsIContent 137 * 138 * XXX document the conditions under which these may fail, and what they 139 * return in those cases. 140 */ 141 static float GetFontSize(const dom::Element* aElement); 142 static float GetFontSize(const nsIFrame* aFrame); 143 static float GetFontSize(const ComputedStyle*, nsPresContext*); 144 /* 145 * Get the number of CSS px (user units) per ex (i.e. the x-height in user 146 * units) for an nsIContent 147 * 148 * XXX document the conditions under which these may fail, and what they 149 * return in those cases. 150 */ 151 static float GetFontXHeight(const dom::Element* aElement); 152 static float GetFontXHeight(const nsIFrame* aFrame); 153 static float GetFontXHeight(const ComputedStyle*, nsPresContext*); 154 155 /* 156 * Get the number of CSS px (user units) per lh (i.e. the line-height in 157 * user units) for an nsIContent. 158 * 159 * Requires the element be styled - if not, a default value assuming 160 * the font-size of 16px and line-height of 1.2 is returned. 161 */ 162 static float GetLineHeight(const dom::Element* aElement); 163 164 /* 165 * Report a localized error message to the error console. 166 */ 167 static nsresult ReportToConsole(const dom::Document* doc, 168 const char* aWarning, 169 const nsTArray<nsString>& aParams); 170 171 static Matrix GetCTM(dom::SVGElement* aElement); 172 173 static Matrix GetNonScalingStrokeCTM(dom::SVGElement* aElement); 174 175 static Matrix GetScreenCTM(dom::SVGElement* aElement); 176 177 /** 178 * Gets the tight bounds-space stroke bounds of the non-scaling-stroked rect 179 * aRect. 180 * @param aToBoundsSpace transforms from source space to the space aBounds 181 * should be computed in. Must be rectilinear. 182 * @param aToNonScalingStrokeSpace transforms from source 183 * space to the space in which non-scaling stroke should be applied. 184 * Must be rectilinear. 185 */ 186 static void RectilinearGetStrokeBounds(const Rect& aRect, 187 const Matrix& aToBoundsSpace, 188 const Matrix& aToNonScalingStrokeSpace, 189 float aStrokeWidth, Rect* aBounds); 190 191 static dom::SVGViewportElement* GetNearestViewportElement( 192 const nsIContent* aContent); 193 194 /* enum for specifying coordinate direction for ObjectSpace/UserSpace */ 195 enum ctxDirection { X, Y, XY }; 196 197 /** 198 * Computes sqrt((aWidth^2 + aHeight^2)/2); 199 */ 200 static double ComputeNormalizedHypotenuse(double aWidth, double aHeight); 201 202 /* Returns the angle halfway between the two specified angles */ 203 static float AngleBisect(float a1, float a2); 204 205 /* Generate a viewbox to viewport transformation matrix */ 206 207 static Matrix GetViewBoxTransform( 208 float aViewportWidth, float aViewportHeight, float aViewboxX, 209 float aViewboxY, float aViewboxWidth, float aViewboxHeight, 210 const SVGAnimatedPreserveAspectRatio& aPreserveAspectRatio); 211 212 static Matrix GetViewBoxTransform( 213 float aViewportWidth, float aViewportHeight, float aViewboxX, 214 float aViewboxY, float aViewboxWidth, float aViewboxHeight, 215 const SVGPreserveAspectRatio& aPreserveAspectRatio); 216 217 /** 218 * Parses the sign (+ or -) of a number and moves aIter to the next 219 * character if a sign is found. 220 * @param aSignMultiplier [outparam] -1 if the sign is negative otherwise 1 221 * @return false if we hit the end of the string (i.e. if aIter is initially 222 * at aEnd, or if we reach aEnd right after the sign character). 223 */ 224 static inline bool ParseOptionalSign(nsAString::const_iterator& aIter, 225 const nsAString::const_iterator& aEnd, 226 int32_t& aSignMultiplier) { 227 if (aIter == aEnd) { 228 return false; 229 } 230 aSignMultiplier = *aIter == '-' ? -1 : 1; 231 232 nsAString::const_iterator iter(aIter); 233 234 if (*iter == '-' || *iter == '+') { 235 ++iter; 236 if (iter == aEnd) { 237 return false; 238 } 239 } 240 aIter = iter; 241 return true; 242 } 243 244 /** 245 * Parse a number of the form: 246 * number ::= integer ([Ee] integer)? | [+-]? [0-9]* "." [0-9]+ ([Ee] 247 * integer)? Parsing fails if the number cannot be represented by a floatType. 248 * If parsing succeeds, aIter is updated so that it points to the character 249 * after the end of the number, otherwise it is left unchanged 250 */ 251 template <class floatType> 252 static bool ParseNumber(nsAString::const_iterator& aIter, 253 const nsAString::const_iterator& aEnd, 254 floatType& aValue); 255 256 /** 257 * Parse a number of the form: 258 * number ::= integer ([Ee] integer)? | [+-]? [0-9]* "." [0-9]+ ([Ee] 259 * integer)? Parsing fails if there is anything left over after the number, or 260 * the number cannot be represented by a floatType. 261 */ 262 template <class floatType> 263 static bool ParseNumber(const nsAString& aString, floatType& aValue); 264 265 /** 266 * Parse an integer of the form: 267 * integer ::= [+-]? [0-9]+ 268 * The returned number is clamped to an int32_t if outside that range. 269 * If parsing succeeds, aIter is updated so that it points to the character 270 * after the end of the number, otherwise it is left unchanged 271 */ 272 static bool ParseInteger(nsAString::const_iterator& aIter, 273 const nsAString::const_iterator& aEnd, 274 int32_t& aValue); 275 276 /** 277 * Parse an integer of the form: 278 * integer ::= [+-]? [0-9]+ 279 * The returned number is clamped to an int32_t if outside that range. 280 * Parsing fails if there is anything left over after the number. 281 */ 282 static bool ParseInteger(const nsAString& aString, int32_t& aValue); 283 284 // XXX This should rather use LengthPercentage instead of 285 // StyleLengthPercentageUnion, but that's a type alias defined in 286 // ServoStyleConsts.h, and we don't want to avoid including that large header 287 // with all its dependencies. If a forwarding header were generated by 288 // cbindgen, we could include that. 289 // https://github.com/eqrion/cbindgen/issues/617 addresses this. 290 /** 291 * Converts a LengthPercentage into a userspace value, resolving percentage 292 * values relative to aContent's SVG viewport. 293 */ 294 static float CoordToFloat(const dom::SVGElement* aContent, 295 const StyleLengthPercentageUnion&, 296 uint8_t aCtxType = SVGContentUtils::XY); 297 /** 298 * Parse the SVG path string 299 * Returns a path 300 * string formatted as an SVG path 301 */ 302 static already_AddRefed<gfx::Path> GetPath(const nsACString& aPathString); 303 304 /** 305 * Returns true if aContent is one of the elements whose stroke is guaranteed 306 * to have no corners: circle or ellipse 307 */ 308 static bool ShapeTypeHasNoCorners(const nsIContent* aContent); 309 310 /** 311 * Return one token in aString, aString may have leading and trailing 312 * whitespace; aSuccess will be set to false if there is no token or more than 313 * one token, otherwise it's set to true. 314 */ 315 static nsDependentSubstring GetAndEnsureOneToken(const nsAString& aString, 316 bool& aSuccess); 317 }; 318 319 } // namespace mozilla 320 321 #endif // DOM_SVG_SVGCONTENTUTILS_H_