Bug 1444460 [wpt PR 9948] - gyroscope: Rename LocalCoordinateSystem to GyroscopeLocal...
[gecko.git] / dom / svg / SVGContentUtils.cpp
blobd1c706d5d751d517e0e9b4fe6de688277a9d0f6f
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/. */
7 // Main header first:
8 // This is also necessary to ensure our definition of M_SQRT1_2 is picked up
9 #include "SVGContentUtils.h"
11 // Keep others in (case-insensitive) order:
12 #include "gfx2DGlue.h"
13 #include "gfxMatrix.h"
14 #include "gfxPlatform.h"
15 #include "mozilla/gfx/2D.h"
16 #include "mozilla/dom/SVGSVGElement.h"
17 #include "mozilla/RefPtr.h"
18 #include "mozilla/SVGContextPaint.h"
19 #include "nsComputedDOMStyle.h"
20 #include "nsFontMetrics.h"
21 #include "nsIFrame.h"
22 #include "nsIScriptError.h"
23 #include "nsLayoutUtils.h"
24 #include "nsMathUtils.h"
25 #include "SVGAnimationElement.h"
26 #include "SVGAnimatedPreserveAspectRatio.h"
27 #include "nsContentUtils.h"
28 #include "mozilla/gfx/2D.h"
29 #include "mozilla/gfx/Types.h"
30 #include "mozilla/FloatingPoint.h"
31 #include "mozilla/ComputedStyle.h"
32 #include "nsSVGPathDataParser.h"
33 #include "SVGPathData.h"
34 #include "SVGPathElement.h"
36 using namespace mozilla;
37 using namespace mozilla::dom;
38 using namespace mozilla::dom::SVGPreserveAspectRatioBinding;
39 using namespace mozilla::gfx;
41 SVGSVGElement*
42 SVGContentUtils::GetOuterSVGElement(nsSVGElement *aSVGElement)
44 nsIContent *element = nullptr;
45 nsIContent *ancestor = aSVGElement->GetFlattenedTreeParent();
47 while (ancestor && ancestor->IsSVGElement() &&
48 !ancestor->IsSVGElement(nsGkAtoms::foreignObject)) {
49 element = ancestor;
50 ancestor = element->GetFlattenedTreeParent();
53 if (element && element->IsSVGElement(nsGkAtoms::svg)) {
54 return static_cast<SVGSVGElement*>(element);
56 return nullptr;
59 void
60 SVGContentUtils::ActivateByHyperlink(nsIContent *aContent)
62 MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eANIMATION),
63 "Expecting an animation element");
65 static_cast<SVGAnimationElement*>(aContent)->ActivateByHyperlink();
68 enum DashState {
69 eDashedStroke,
70 eContinuousStroke, //< all dashes, no gaps
71 eNoStroke //< all gaps, no dashes
74 static DashState
75 GetStrokeDashData(SVGContentUtils::AutoStrokeOptions* aStrokeOptions,
76 nsSVGElement* aElement,
77 const nsStyleSVG* aStyleSVG,
78 SVGContextPaint* aContextPaint)
80 size_t dashArrayLength;
81 Float totalLengthOfDashes = 0.0, totalLengthOfGaps = 0.0;
82 Float pathScale = 1.0;
84 if (aContextPaint && aStyleSVG->StrokeDasharrayFromObject()) {
85 const FallibleTArray<Float>& dashSrc = aContextPaint->GetStrokeDashArray();
86 dashArrayLength = dashSrc.Length();
87 if (dashArrayLength <= 0) {
88 return eContinuousStroke;
90 Float* dashPattern = aStrokeOptions->InitDashPattern(dashArrayLength);
91 if (!dashPattern) {
92 return eContinuousStroke;
94 for (size_t i = 0; i < dashArrayLength; i++) {
95 if (dashSrc[i] < 0.0) {
96 return eContinuousStroke; // invalid
98 dashPattern[i] = Float(dashSrc[i]);
99 (i % 2 ? totalLengthOfGaps : totalLengthOfDashes) += dashSrc[i];
101 } else {
102 const nsTArray<nsStyleCoord>& dasharray = aStyleSVG->mStrokeDasharray;
103 dashArrayLength = aStyleSVG->mStrokeDasharray.Length();
104 if (dashArrayLength <= 0) {
105 return eContinuousStroke;
107 if (aElement->IsNodeOfType(nsINode::eSHAPE)) {
108 pathScale = static_cast<SVGGeometryElement*>(aElement)->
109 GetPathLengthScale(SVGGeometryElement::eForStroking);
110 if (pathScale <= 0) {
111 return eContinuousStroke;
114 Float* dashPattern = aStrokeOptions->InitDashPattern(dashArrayLength);
115 if (!dashPattern) {
116 return eContinuousStroke;
118 for (uint32_t i = 0; i < dashArrayLength; i++) {
119 Float dashLength =
120 SVGContentUtils::CoordToFloat(aElement, dasharray[i]) * pathScale;
121 if (dashLength < 0.0) {
122 return eContinuousStroke; // invalid
124 dashPattern[i] = dashLength;
125 (i % 2 ? totalLengthOfGaps : totalLengthOfDashes) += dashLength;
129 // Now that aStrokeOptions.mDashPattern is fully initialized (we didn't
130 // return early above) we can safely set mDashLength:
131 aStrokeOptions->mDashLength = dashArrayLength;
133 if ((dashArrayLength % 2) == 1) {
134 // If we have a dash pattern with an odd number of lengths the pattern
135 // repeats a second time, per the SVG spec., and as implemented by Moz2D.
136 // When deciding whether to return eNoStroke or eContinuousStroke below we
137 // need to take into account that in the repeat pattern the dashes become
138 // gaps, and the gaps become dashes.
139 Float origTotalLengthOfDashes = totalLengthOfDashes;
140 totalLengthOfDashes += totalLengthOfGaps;
141 totalLengthOfGaps += origTotalLengthOfDashes;
144 // Stroking using dashes is much slower than stroking a continuous line
145 // (see bug 609361 comment 40), and much, much slower than not stroking the
146 // line at all. Here we check for cases when the dash pattern causes the
147 // stroke to essentially be continuous or to be nonexistent in which case
148 // we can avoid expensive stroking operations (the underlying platform
149 // graphics libraries don't seem to optimize for this).
150 if (totalLengthOfGaps <= 0) {
151 return eContinuousStroke;
153 // We can only return eNoStroke if the value of stroke-linecap isn't
154 // adding caps to zero length dashes.
155 if (totalLengthOfDashes <= 0 &&
156 aStyleSVG->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_BUTT) {
157 return eNoStroke;
160 if (aContextPaint && aStyleSVG->StrokeDashoffsetFromObject()) {
161 aStrokeOptions->mDashOffset = Float(aContextPaint->GetStrokeDashOffset());
162 } else {
163 aStrokeOptions->mDashOffset =
164 SVGContentUtils::CoordToFloat(aElement, aStyleSVG->mStrokeDashoffset) *
165 pathScale;
168 return eDashedStroke;
171 void
172 SVGContentUtils::GetStrokeOptions(AutoStrokeOptions* aStrokeOptions,
173 nsSVGElement* aElement,
174 ComputedStyle* aComputedStyle,
175 SVGContextPaint* aContextPaint,
176 StrokeOptionFlags aFlags)
178 RefPtr<ComputedStyle> computedStyle;
179 if (aComputedStyle) {
180 computedStyle = aComputedStyle;
181 } else {
182 computedStyle =
183 nsComputedDOMStyle::GetComputedStyleNoFlush(aElement, nullptr);
186 if (!computedStyle) {
187 return;
190 const nsStyleSVG* styleSVG = computedStyle->StyleSVG();
192 bool checkedDashAndStrokeIsDashed = false;
193 if (aFlags != eIgnoreStrokeDashing) {
194 DashState dashState =
195 GetStrokeDashData(aStrokeOptions, aElement, styleSVG, aContextPaint);
197 if (dashState == eNoStroke) {
198 // Hopefully this will shortcircuit any stroke operations:
199 aStrokeOptions->mLineWidth = 0;
200 return;
202 if (dashState == eContinuousStroke && aStrokeOptions->mDashPattern) {
203 // Prevent our caller from wasting time looking at a pattern without gaps:
204 aStrokeOptions->DiscardDashPattern();
206 checkedDashAndStrokeIsDashed = (dashState == eDashedStroke);
209 aStrokeOptions->mLineWidth =
210 GetStrokeWidth(aElement, computedStyle, aContextPaint);
212 aStrokeOptions->mMiterLimit = Float(styleSVG->mStrokeMiterlimit);
214 switch (styleSVG->mStrokeLinejoin) {
215 case NS_STYLE_STROKE_LINEJOIN_MITER:
216 aStrokeOptions->mLineJoin = JoinStyle::MITER_OR_BEVEL;
217 break;
218 case NS_STYLE_STROKE_LINEJOIN_ROUND:
219 aStrokeOptions->mLineJoin = JoinStyle::ROUND;
220 break;
221 case NS_STYLE_STROKE_LINEJOIN_BEVEL:
222 aStrokeOptions->mLineJoin = JoinStyle::BEVEL;
223 break;
226 if (ShapeTypeHasNoCorners(aElement) && !checkedDashAndStrokeIsDashed) {
227 // Note: if aFlags == eIgnoreStrokeDashing then we may be returning the
228 // wrong linecap value here, since the actual linecap used on render in this
229 // case depends on whether the stroke is dashed or not.
230 aStrokeOptions->mLineCap = CapStyle::BUTT;
231 } else {
232 switch (styleSVG->mStrokeLinecap) {
233 case NS_STYLE_STROKE_LINECAP_BUTT:
234 aStrokeOptions->mLineCap = CapStyle::BUTT;
235 break;
236 case NS_STYLE_STROKE_LINECAP_ROUND:
237 aStrokeOptions->mLineCap = CapStyle::ROUND;
238 break;
239 case NS_STYLE_STROKE_LINECAP_SQUARE:
240 aStrokeOptions->mLineCap = CapStyle::SQUARE;
241 break;
246 Float
247 SVGContentUtils::GetStrokeWidth(nsSVGElement* aElement,
248 ComputedStyle* aComputedStyle,
249 SVGContextPaint* aContextPaint)
251 RefPtr<ComputedStyle> computedStyle;
252 if (aComputedStyle) {
253 computedStyle = aComputedStyle;
254 } else {
255 computedStyle =
256 nsComputedDOMStyle::GetComputedStyleNoFlush(aElement, nullptr);
259 if (!computedStyle) {
260 return 0.0f;
263 const nsStyleSVG* styleSVG = computedStyle->StyleSVG();
265 if (aContextPaint && styleSVG->StrokeWidthFromObject()) {
266 return aContextPaint->GetStrokeWidth();
269 return SVGContentUtils::CoordToFloat(aElement, styleSVG->mStrokeWidth);
272 float
273 SVGContentUtils::GetFontSize(Element *aElement)
275 if (!aElement)
276 return 1.0f;
278 RefPtr<ComputedStyle> computedStyle =
279 nsComputedDOMStyle::GetComputedStyleNoFlush(aElement, nullptr);
280 if (!computedStyle) {
281 // ReportToConsole
282 NS_WARNING("Couldn't get ComputedStyle for content in GetFontStyle");
283 return 1.0f;
286 return GetFontSize(computedStyle);
289 float
290 SVGContentUtils::GetFontSize(nsIFrame *aFrame)
292 MOZ_ASSERT(aFrame, "NULL frame in GetFontSize");
293 return GetFontSize(aFrame->Style());
296 float
297 SVGContentUtils::GetFontSize(ComputedStyle *aComputedStyle)
299 MOZ_ASSERT(aComputedStyle, "NULL ComputedStyle in GetFontSize");
301 nsPresContext *presContext = aComputedStyle->PresContext();
302 MOZ_ASSERT(presContext, "NULL pres context in GetFontSize");
304 nscoord fontSize = aComputedStyle->StyleFont()->mSize;
305 return nsPresContext::AppUnitsToFloatCSSPixels(fontSize) /
306 presContext->EffectiveTextZoom();
309 float
310 SVGContentUtils::GetFontXHeight(Element *aElement)
312 if (!aElement)
313 return 1.0f;
315 RefPtr<ComputedStyle> computedStyle =
316 nsComputedDOMStyle::GetComputedStyleNoFlush(aElement, nullptr);
317 if (!computedStyle) {
318 // ReportToConsole
319 NS_WARNING("Couldn't get ComputedStyle for content in GetFontStyle");
320 return 1.0f;
323 return GetFontXHeight(computedStyle);
326 float
327 SVGContentUtils::GetFontXHeight(nsIFrame *aFrame)
329 MOZ_ASSERT(aFrame, "NULL frame in GetFontXHeight");
330 return GetFontXHeight(aFrame->Style());
333 float
334 SVGContentUtils::GetFontXHeight(ComputedStyle *aComputedStyle)
336 MOZ_ASSERT(aComputedStyle, "NULL ComputedStyle in GetFontXHeight");
338 nsPresContext *presContext = aComputedStyle->PresContext();
339 MOZ_ASSERT(presContext, "NULL pres context in GetFontXHeight");
341 RefPtr<nsFontMetrics> fontMetrics =
342 nsLayoutUtils::GetFontMetricsForComputedStyle(aComputedStyle);
344 if (!fontMetrics) {
345 // ReportToConsole
346 NS_WARNING("no FontMetrics in GetFontXHeight()");
347 return 1.0f;
350 nscoord xHeight = fontMetrics->XHeight();
351 return nsPresContext::AppUnitsToFloatCSSPixels(xHeight) /
352 presContext->EffectiveTextZoom();
354 nsresult
355 SVGContentUtils::ReportToConsole(nsIDocument* doc,
356 const char* aWarning,
357 const char16_t **aParams,
358 uint32_t aParamsLength)
360 return nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
361 NS_LITERAL_CSTRING("SVG"), doc,
362 nsContentUtils::eSVG_PROPERTIES,
363 aWarning,
364 aParams, aParamsLength);
367 bool
368 SVGContentUtils::EstablishesViewport(nsIContent *aContent)
370 // Although SVG 1.1 states that <image> is an element that establishes a
371 // viewport, this is really only for the document it references, not
372 // for any child content, which is what this function is used for.
373 return aContent && aContent->IsAnyOfSVGElements(nsGkAtoms::svg,
374 nsGkAtoms::foreignObject,
375 nsGkAtoms::symbol);
378 SVGViewportElement*
379 SVGContentUtils::GetNearestViewportElement(const nsIContent *aContent)
381 nsIContent *element = aContent->GetFlattenedTreeParent();
383 while (element && element->IsSVGElement()) {
384 if (EstablishesViewport(element)) {
385 if (element->IsSVGElement(nsGkAtoms::foreignObject)) {
386 return nullptr;
388 MOZ_ASSERT(element->IsAnyOfSVGElements(nsGkAtoms::svg,
389 nsGkAtoms::symbol),
390 "upcoming static_cast is only valid for "
391 "SVGViewportElement subclasses");
392 return static_cast<SVGViewportElement*>(element);
394 element = element->GetFlattenedTreeParent();
396 return nullptr;
399 static gfx::Matrix
400 GetCTMInternal(nsSVGElement *aElement, bool aScreenCTM, bool aHaveRecursed)
402 gfxMatrix matrix = aElement->PrependLocalTransformsTo(gfxMatrix(),
403 aHaveRecursed ? eAllTransforms : eUserSpaceToParent);
404 nsSVGElement *element = aElement;
405 nsIContent *ancestor = aElement->GetFlattenedTreeParent();
407 while (ancestor && ancestor->IsSVGElement() &&
408 !ancestor->IsSVGElement(nsGkAtoms::foreignObject)) {
409 element = static_cast<nsSVGElement*>(ancestor);
410 matrix *= element->PrependLocalTransformsTo(gfxMatrix()); // i.e. *A*ppend
411 if (!aScreenCTM && SVGContentUtils::EstablishesViewport(element)) {
412 if (!element->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG) &&
413 !element->NodeInfo()->Equals(nsGkAtoms::symbol, kNameSpaceID_SVG)) {
414 NS_ERROR("New (SVG > 1.1) SVG viewport establishing element?");
415 return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
417 // XXX spec seems to say x,y translation should be undone for IsInnerSVG
418 return gfx::ToMatrix(matrix);
420 ancestor = ancestor->GetFlattenedTreeParent();
422 if (!aScreenCTM) {
423 // didn't find a nearestViewportElement
424 return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
426 if (!element->IsSVGElement(nsGkAtoms::svg)) {
427 // Not a valid SVG fragment
428 return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
430 if (element == aElement && !aHaveRecursed) {
431 // We get here when getScreenCTM() is called on an outer-<svg>.
432 // Consistency with other elements would have us include only the
433 // eFromUserSpace transforms, but we include the eAllTransforms
434 // transforms in this case since that's what we've been doing for
435 // a while, and it keeps us consistent with WebKit and Opera (if not
436 // really with the ambiguous spec).
437 matrix = aElement->PrependLocalTransformsTo(gfxMatrix());
439 if (!ancestor || !ancestor->IsElement()) {
440 return gfx::ToMatrix(matrix);
442 if (ancestor->IsSVGElement()) {
443 return
444 gfx::ToMatrix(matrix) * GetCTMInternal(static_cast<nsSVGElement*>(ancestor), true, true);
447 // XXX this does not take into account CSS transform, or that the non-SVG
448 // content that we've hit may itself be inside an SVG foreignObject higher up
449 nsIDocument* currentDoc = aElement->GetComposedDoc();
450 float x = 0.0f, y = 0.0f;
451 if (currentDoc && element->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG)) {
452 nsIPresShell *presShell = currentDoc->GetShell();
453 if (presShell) {
454 nsIFrame* frame = element->GetPrimaryFrame();
455 nsIFrame* ancestorFrame = presShell->GetRootFrame();
456 if (frame && ancestorFrame) {
457 nsPoint point = frame->GetOffsetTo(ancestorFrame);
458 x = nsPresContext::AppUnitsToFloatCSSPixels(point.x);
459 y = nsPresContext::AppUnitsToFloatCSSPixels(point.y);
463 return ToMatrix(matrix).PostTranslate(x, y);
466 gfx::Matrix
467 SVGContentUtils::GetCTM(nsSVGElement *aElement, bool aScreenCTM)
469 return GetCTMInternal(aElement, aScreenCTM, false);
472 void
473 SVGContentUtils::RectilinearGetStrokeBounds(const Rect& aRect,
474 const Matrix& aToBoundsSpace,
475 const Matrix& aToNonScalingStrokeSpace,
476 float aStrokeWidth,
477 Rect* aBounds)
479 MOZ_ASSERT(aToBoundsSpace.IsRectilinear(),
480 "aToBoundsSpace must be rectilinear");
481 MOZ_ASSERT(aToNonScalingStrokeSpace.IsRectilinear(),
482 "aToNonScalingStrokeSpace must be rectilinear");
484 Matrix nonScalingToSource = aToNonScalingStrokeSpace.Inverse();
485 Matrix nonScalingToBounds = nonScalingToSource * aToBoundsSpace;
487 *aBounds = aToBoundsSpace.TransformBounds(aRect);
489 // Compute the amounts dx and dy that nonScalingToBounds scales a half-width
490 // stroke in the x and y directions, and then inflate aBounds by those amounts
491 // so that when aBounds is transformed back to non-scaling-stroke space
492 // it will map onto the correct stroked bounds.
494 Float dx = 0.0f;
495 Float dy = 0.0f;
496 // nonScalingToBounds is rectilinear, so either _12 and _21 are zero or _11
497 // and _22 are zero, and in each case the non-zero entries (from among _11,
498 // _12, _21, _22) simply scale the stroke width in the x and y directions.
499 if (FuzzyEqual(nonScalingToBounds._12, 0) &&
500 FuzzyEqual(nonScalingToBounds._21, 0)) {
501 dx = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._11);
502 dy = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._22);
503 } else {
504 dx = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._21);
505 dy = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._12);
508 aBounds->Inflate(dx, dy);
511 double
512 SVGContentUtils::ComputeNormalizedHypotenuse(double aWidth, double aHeight)
514 return NS_hypot(aWidth, aHeight) / M_SQRT2;
517 float
518 SVGContentUtils::AngleBisect(float a1, float a2)
520 float delta = fmod(a2 - a1, static_cast<float>(2*M_PI));
521 if (delta < 0) {
522 delta += static_cast<float>(2*M_PI);
524 /* delta is now the angle from a1 around to a2, in the range [0, 2*M_PI) */
525 float r = a1 + delta/2;
526 if (delta >= M_PI) {
527 /* the arc from a2 to a1 is smaller, so use the ray on that side */
528 r += static_cast<float>(M_PI);
530 return r;
533 gfx::Matrix
534 SVGContentUtils::GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
535 float aViewboxX, float aViewboxY,
536 float aViewboxWidth, float aViewboxHeight,
537 const SVGAnimatedPreserveAspectRatio &aPreserveAspectRatio)
539 return GetViewBoxTransform(aViewportWidth, aViewportHeight,
540 aViewboxX, aViewboxY,
541 aViewboxWidth, aViewboxHeight,
542 aPreserveAspectRatio.GetAnimValue());
545 gfx::Matrix
546 SVGContentUtils::GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
547 float aViewboxX, float aViewboxY,
548 float aViewboxWidth, float aViewboxHeight,
549 const SVGPreserveAspectRatio &aPreserveAspectRatio)
551 NS_ASSERTION(aViewportWidth >= 0, "viewport width must be nonnegative!");
552 NS_ASSERTION(aViewportHeight >= 0, "viewport height must be nonnegative!");
553 NS_ASSERTION(aViewboxWidth > 0, "viewBox width must be greater than zero!");
554 NS_ASSERTION(aViewboxHeight > 0, "viewBox height must be greater than zero!");
556 uint16_t align = aPreserveAspectRatio.GetAlign();
557 uint16_t meetOrSlice = aPreserveAspectRatio.GetMeetOrSlice();
559 // default to the defaults
560 if (align == SVG_PRESERVEASPECTRATIO_UNKNOWN)
561 align = SVG_PRESERVEASPECTRATIO_XMIDYMID;
562 if (meetOrSlice == SVG_MEETORSLICE_UNKNOWN)
563 meetOrSlice = SVG_MEETORSLICE_MEET;
565 float a, d, e, f;
566 a = aViewportWidth / aViewboxWidth;
567 d = aViewportHeight / aViewboxHeight;
568 e = 0.0f;
569 f = 0.0f;
571 if (align != SVG_PRESERVEASPECTRATIO_NONE &&
572 a != d) {
573 if ((meetOrSlice == SVG_MEETORSLICE_MEET && a < d) ||
574 (meetOrSlice == SVG_MEETORSLICE_SLICE && d < a)) {
575 d = a;
576 switch (align) {
577 case SVG_PRESERVEASPECTRATIO_XMINYMIN:
578 case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
579 case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
580 break;
581 case SVG_PRESERVEASPECTRATIO_XMINYMID:
582 case SVG_PRESERVEASPECTRATIO_XMIDYMID:
583 case SVG_PRESERVEASPECTRATIO_XMAXYMID:
584 f = (aViewportHeight - a * aViewboxHeight) / 2.0f;
585 break;
586 case SVG_PRESERVEASPECTRATIO_XMINYMAX:
587 case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
588 case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
589 f = aViewportHeight - a * aViewboxHeight;
590 break;
591 default:
592 NS_NOTREACHED("Unknown value for align");
595 else if (
596 (meetOrSlice == SVG_MEETORSLICE_MEET &&
597 d < a) ||
598 (meetOrSlice == SVG_MEETORSLICE_SLICE &&
599 a < d)) {
600 a = d;
601 switch (align) {
602 case SVG_PRESERVEASPECTRATIO_XMINYMIN:
603 case SVG_PRESERVEASPECTRATIO_XMINYMID:
604 case SVG_PRESERVEASPECTRATIO_XMINYMAX:
605 break;
606 case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
607 case SVG_PRESERVEASPECTRATIO_XMIDYMID:
608 case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
609 e = (aViewportWidth - a * aViewboxWidth) / 2.0f;
610 break;
611 case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
612 case SVG_PRESERVEASPECTRATIO_XMAXYMID:
613 case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
614 e = aViewportWidth - a * aViewboxWidth;
615 break;
616 default:
617 NS_NOTREACHED("Unknown value for align");
620 else NS_NOTREACHED("Unknown value for meetOrSlice");
623 if (aViewboxX) e += -a * aViewboxX;
624 if (aViewboxY) f += -d * aViewboxY;
626 return gfx::Matrix(a, 0.0f, 0.0f, d, e, f);
629 static bool
630 ParseNumber(RangedPtr<const char16_t>& aIter,
631 const RangedPtr<const char16_t>& aEnd,
632 double& aValue)
634 int32_t sign;
635 if (!SVGContentUtils::ParseOptionalSign(aIter, aEnd, sign)) {
636 return false;
639 // Absolute value of the integer part of the mantissa.
640 double intPart = 0.0;
642 bool gotDot = *aIter == '.';
644 if (!gotDot) {
645 if (!SVGContentUtils::IsDigit(*aIter)) {
646 return false;
648 do {
649 intPart = 10.0 * intPart + SVGContentUtils::DecimalDigitValue(*aIter);
650 ++aIter;
651 } while (aIter != aEnd && SVGContentUtils::IsDigit(*aIter));
653 if (aIter != aEnd) {
654 gotDot = *aIter == '.';
658 // Fractional part of the mantissa.
659 double fracPart = 0.0;
661 if (gotDot) {
662 ++aIter;
663 if (aIter == aEnd || !SVGContentUtils::IsDigit(*aIter)) {
664 return false;
667 // Power of ten by which we need to divide the fraction
668 double divisor = 1.0;
670 do {
671 fracPart = 10.0 * fracPart + SVGContentUtils::DecimalDigitValue(*aIter);
672 divisor *= 10.0;
673 ++aIter;
674 } while (aIter != aEnd && SVGContentUtils::IsDigit(*aIter));
676 fracPart /= divisor;
679 bool gotE = false;
680 int32_t exponent = 0;
681 int32_t expSign;
683 if (aIter != aEnd && (*aIter == 'e' || *aIter == 'E')) {
685 RangedPtr<const char16_t> expIter(aIter);
687 ++expIter;
688 if (expIter != aEnd) {
689 expSign = *expIter == '-' ? -1 : 1;
690 if (*expIter == '-' || *expIter == '+') {
691 ++expIter;
693 if (expIter != aEnd && SVGContentUtils::IsDigit(*expIter)) {
694 // At this point we're sure this is an exponent
695 // and not the start of a unit such as em or ex.
696 gotE = true;
700 if (gotE) {
701 aIter = expIter;
702 do {
703 exponent = 10.0 * exponent + SVGContentUtils::DecimalDigitValue(*aIter);
704 ++aIter;
705 } while (aIter != aEnd && SVGContentUtils::IsDigit(*aIter));
709 // Assemble the number
710 aValue = sign * (intPart + fracPart);
711 if (gotE) {
712 aValue *= pow(10.0, expSign * exponent);
714 return true;
717 template<class floatType>
718 bool
719 SVGContentUtils::ParseNumber(RangedPtr<const char16_t>& aIter,
720 const RangedPtr<const char16_t>& aEnd,
721 floatType& aValue)
723 RangedPtr<const char16_t> iter(aIter);
725 double value;
726 if (!::ParseNumber(iter, aEnd, value)) {
727 return false;
729 floatType floatValue = floatType(value);
730 if (!IsFinite(floatValue)) {
731 return false;
733 aValue = floatValue;
734 aIter = iter;
735 return true;
738 template bool
739 SVGContentUtils::ParseNumber<float>(RangedPtr<const char16_t>& aIter,
740 const RangedPtr<const char16_t>& aEnd,
741 float& aValue);
743 template bool
744 SVGContentUtils::ParseNumber<double>(RangedPtr<const char16_t>& aIter,
745 const RangedPtr<const char16_t>& aEnd,
746 double& aValue);
748 RangedPtr<const char16_t>
749 SVGContentUtils::GetStartRangedPtr(const nsAString& aString)
751 return RangedPtr<const char16_t>(aString.Data(), aString.Length());
754 RangedPtr<const char16_t>
755 SVGContentUtils::GetEndRangedPtr(const nsAString& aString)
757 return RangedPtr<const char16_t>(aString.Data() + aString.Length(),
758 aString.Data(), aString.Length());
761 template<class floatType>
762 bool
763 SVGContentUtils::ParseNumber(const nsAString& aString,
764 floatType& aValue)
766 RangedPtr<const char16_t> iter = GetStartRangedPtr(aString);
767 const RangedPtr<const char16_t> end = GetEndRangedPtr(aString);
769 return ParseNumber(iter, end, aValue) && iter == end;
772 template bool
773 SVGContentUtils::ParseNumber<float>(const nsAString& aString,
774 float& aValue);
775 template bool
776 SVGContentUtils::ParseNumber<double>(const nsAString& aString,
777 double& aValue);
779 /* static */
780 bool
781 SVGContentUtils::ParseInteger(RangedPtr<const char16_t>& aIter,
782 const RangedPtr<const char16_t>& aEnd,
783 int32_t& aValue)
785 RangedPtr<const char16_t> iter(aIter);
787 int32_t sign;
788 if (!ParseOptionalSign(iter, aEnd, sign)) {
789 return false;
792 if (!IsDigit(*iter)) {
793 return false;
796 int64_t value = 0;
798 do {
799 if (value <= std::numeric_limits<int32_t>::max()) {
800 value = 10 * value + DecimalDigitValue(*iter);
802 ++iter;
803 } while (iter != aEnd && IsDigit(*iter));
805 aIter = iter;
806 aValue = int32_t(clamped(sign * value,
807 int64_t(std::numeric_limits<int32_t>::min()),
808 int64_t(std::numeric_limits<int32_t>::max())));
809 return true;
812 /* static */
813 bool
814 SVGContentUtils::ParseInteger(const nsAString& aString,
815 int32_t& aValue)
817 RangedPtr<const char16_t> iter = GetStartRangedPtr(aString);
818 const RangedPtr<const char16_t> end = GetEndRangedPtr(aString);
820 return ParseInteger(iter, end, aValue) && iter == end;
823 float
824 SVGContentUtils::CoordToFloat(nsSVGElement *aContent,
825 const nsStyleCoord &aCoord)
827 switch (aCoord.GetUnit()) {
828 case eStyleUnit_Factor:
829 // user units
830 return aCoord.GetFactorValue();
832 case eStyleUnit_Coord:
833 return nsPresContext::AppUnitsToFloatCSSPixels(aCoord.GetCoordValue());
835 case eStyleUnit_Percent: {
836 SVGViewportElement* ctx = aContent->GetCtx();
837 return ctx ? aCoord.GetPercentValue() * ctx->GetLength(SVGContentUtils::XY) : 0.0f;
839 default:
840 return 0.0f;
844 already_AddRefed<gfx::Path>
845 SVGContentUtils::GetPath(const nsAString& aPathString)
847 SVGPathData pathData;
848 nsSVGPathDataParser parser(aPathString, &pathData);
849 if (!parser.Parse()) {
850 return NULL;
853 RefPtr<DrawTarget> drawTarget =
854 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
855 RefPtr<PathBuilder> builder =
856 drawTarget->CreatePathBuilder(FillRule::FILL_WINDING);
858 return pathData.BuildPath(builder, NS_STYLE_STROKE_LINECAP_BUTT, 1);
861 bool
862 SVGContentUtils::ShapeTypeHasNoCorners(const nsIContent* aContent) {
863 return aContent && aContent->IsAnyOfSVGElements(nsGkAtoms::circle,
864 nsGkAtoms::ellipse);