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/. */
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"
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
;
42 SVGContentUtils::GetOuterSVGElement(nsSVGElement
*aSVGElement
)
44 nsIContent
*element
= nullptr;
45 nsIContent
*ancestor
= aSVGElement
->GetFlattenedTreeParent();
47 while (ancestor
&& ancestor
->IsSVGElement() &&
48 !ancestor
->IsSVGElement(nsGkAtoms::foreignObject
)) {
50 ancestor
= element
->GetFlattenedTreeParent();
53 if (element
&& element
->IsSVGElement(nsGkAtoms::svg
)) {
54 return static_cast<SVGSVGElement
*>(element
);
60 SVGContentUtils::ActivateByHyperlink(nsIContent
*aContent
)
62 MOZ_ASSERT(aContent
->IsNodeOfType(nsINode::eANIMATION
),
63 "Expecting an animation element");
65 static_cast<SVGAnimationElement
*>(aContent
)->ActivateByHyperlink();
70 eContinuousStroke
, //< all dashes, no gaps
71 eNoStroke
//< all gaps, no dashes
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
);
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
];
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
);
116 return eContinuousStroke
;
118 for (uint32_t i
= 0; i
< dashArrayLength
; i
++) {
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
) {
160 if (aContextPaint
&& aStyleSVG
->StrokeDashoffsetFromObject()) {
161 aStrokeOptions
->mDashOffset
= Float(aContextPaint
->GetStrokeDashOffset());
163 aStrokeOptions
->mDashOffset
=
164 SVGContentUtils::CoordToFloat(aElement
, aStyleSVG
->mStrokeDashoffset
) *
168 return eDashedStroke
;
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
;
183 nsComputedDOMStyle::GetComputedStyleNoFlush(aElement
, nullptr);
186 if (!computedStyle
) {
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;
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
;
218 case NS_STYLE_STROKE_LINEJOIN_ROUND
:
219 aStrokeOptions
->mLineJoin
= JoinStyle::ROUND
;
221 case NS_STYLE_STROKE_LINEJOIN_BEVEL
:
222 aStrokeOptions
->mLineJoin
= JoinStyle::BEVEL
;
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
;
232 switch (styleSVG
->mStrokeLinecap
) {
233 case NS_STYLE_STROKE_LINECAP_BUTT
:
234 aStrokeOptions
->mLineCap
= CapStyle::BUTT
;
236 case NS_STYLE_STROKE_LINECAP_ROUND
:
237 aStrokeOptions
->mLineCap
= CapStyle::ROUND
;
239 case NS_STYLE_STROKE_LINECAP_SQUARE
:
240 aStrokeOptions
->mLineCap
= CapStyle::SQUARE
;
247 SVGContentUtils::GetStrokeWidth(nsSVGElement
* aElement
,
248 ComputedStyle
* aComputedStyle
,
249 SVGContextPaint
* aContextPaint
)
251 RefPtr
<ComputedStyle
> computedStyle
;
252 if (aComputedStyle
) {
253 computedStyle
= aComputedStyle
;
256 nsComputedDOMStyle::GetComputedStyleNoFlush(aElement
, nullptr);
259 if (!computedStyle
) {
263 const nsStyleSVG
* styleSVG
= computedStyle
->StyleSVG();
265 if (aContextPaint
&& styleSVG
->StrokeWidthFromObject()) {
266 return aContextPaint
->GetStrokeWidth();
269 return SVGContentUtils::CoordToFloat(aElement
, styleSVG
->mStrokeWidth
);
273 SVGContentUtils::GetFontSize(Element
*aElement
)
278 RefPtr
<ComputedStyle
> computedStyle
=
279 nsComputedDOMStyle::GetComputedStyleNoFlush(aElement
, nullptr);
280 if (!computedStyle
) {
282 NS_WARNING("Couldn't get ComputedStyle for content in GetFontStyle");
286 return GetFontSize(computedStyle
);
290 SVGContentUtils::GetFontSize(nsIFrame
*aFrame
)
292 MOZ_ASSERT(aFrame
, "NULL frame in GetFontSize");
293 return GetFontSize(aFrame
->Style());
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();
310 SVGContentUtils::GetFontXHeight(Element
*aElement
)
315 RefPtr
<ComputedStyle
> computedStyle
=
316 nsComputedDOMStyle::GetComputedStyleNoFlush(aElement
, nullptr);
317 if (!computedStyle
) {
319 NS_WARNING("Couldn't get ComputedStyle for content in GetFontStyle");
323 return GetFontXHeight(computedStyle
);
327 SVGContentUtils::GetFontXHeight(nsIFrame
*aFrame
)
329 MOZ_ASSERT(aFrame
, "NULL frame in GetFontXHeight");
330 return GetFontXHeight(aFrame
->Style());
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
);
346 NS_WARNING("no FontMetrics in GetFontXHeight()");
350 nscoord xHeight
= fontMetrics
->XHeight();
351 return nsPresContext::AppUnitsToFloatCSSPixels(xHeight
) /
352 presContext
->EffectiveTextZoom();
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
,
364 aParams
, aParamsLength
);
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
,
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
)) {
388 MOZ_ASSERT(element
->IsAnyOfSVGElements(nsGkAtoms::svg
,
390 "upcoming static_cast is only valid for "
391 "SVGViewportElement subclasses");
392 return static_cast<SVGViewportElement
*>(element
);
394 element
= element
->GetFlattenedTreeParent();
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();
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()) {
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();
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
);
467 SVGContentUtils::GetCTM(nsSVGElement
*aElement
, bool aScreenCTM
)
469 return GetCTMInternal(aElement
, aScreenCTM
, false);
473 SVGContentUtils::RectilinearGetStrokeBounds(const Rect
& aRect
,
474 const Matrix
& aToBoundsSpace
,
475 const Matrix
& aToNonScalingStrokeSpace
,
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.
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
);
504 dx
= (aStrokeWidth
/ 2.0f
) * std::abs(nonScalingToBounds
._21
);
505 dy
= (aStrokeWidth
/ 2.0f
) * std::abs(nonScalingToBounds
._12
);
508 aBounds
->Inflate(dx
, dy
);
512 SVGContentUtils::ComputeNormalizedHypotenuse(double aWidth
, double aHeight
)
514 return NS_hypot(aWidth
, aHeight
) / M_SQRT2
;
518 SVGContentUtils::AngleBisect(float a1
, float a2
)
520 float delta
= fmod(a2
- a1
, static_cast<float>(2*M_PI
));
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;
527 /* the arc from a2 to a1 is smaller, so use the ray on that side */
528 r
+= static_cast<float>(M_PI
);
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());
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
;
566 a
= aViewportWidth
/ aViewboxWidth
;
567 d
= aViewportHeight
/ aViewboxHeight
;
571 if (align
!= SVG_PRESERVEASPECTRATIO_NONE
&&
573 if ((meetOrSlice
== SVG_MEETORSLICE_MEET
&& a
< d
) ||
574 (meetOrSlice
== SVG_MEETORSLICE_SLICE
&& d
< a
)) {
577 case SVG_PRESERVEASPECTRATIO_XMINYMIN
:
578 case SVG_PRESERVEASPECTRATIO_XMIDYMIN
:
579 case SVG_PRESERVEASPECTRATIO_XMAXYMIN
:
581 case SVG_PRESERVEASPECTRATIO_XMINYMID
:
582 case SVG_PRESERVEASPECTRATIO_XMIDYMID
:
583 case SVG_PRESERVEASPECTRATIO_XMAXYMID
:
584 f
= (aViewportHeight
- a
* aViewboxHeight
) / 2.0f
;
586 case SVG_PRESERVEASPECTRATIO_XMINYMAX
:
587 case SVG_PRESERVEASPECTRATIO_XMIDYMAX
:
588 case SVG_PRESERVEASPECTRATIO_XMAXYMAX
:
589 f
= aViewportHeight
- a
* aViewboxHeight
;
592 NS_NOTREACHED("Unknown value for align");
596 (meetOrSlice
== SVG_MEETORSLICE_MEET
&&
598 (meetOrSlice
== SVG_MEETORSLICE_SLICE
&&
602 case SVG_PRESERVEASPECTRATIO_XMINYMIN
:
603 case SVG_PRESERVEASPECTRATIO_XMINYMID
:
604 case SVG_PRESERVEASPECTRATIO_XMINYMAX
:
606 case SVG_PRESERVEASPECTRATIO_XMIDYMIN
:
607 case SVG_PRESERVEASPECTRATIO_XMIDYMID
:
608 case SVG_PRESERVEASPECTRATIO_XMIDYMAX
:
609 e
= (aViewportWidth
- a
* aViewboxWidth
) / 2.0f
;
611 case SVG_PRESERVEASPECTRATIO_XMAXYMIN
:
612 case SVG_PRESERVEASPECTRATIO_XMAXYMID
:
613 case SVG_PRESERVEASPECTRATIO_XMAXYMAX
:
614 e
= aViewportWidth
- a
* aViewboxWidth
;
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
);
630 ParseNumber(RangedPtr
<const char16_t
>& aIter
,
631 const RangedPtr
<const char16_t
>& aEnd
,
635 if (!SVGContentUtils::ParseOptionalSign(aIter
, aEnd
, sign
)) {
639 // Absolute value of the integer part of the mantissa.
640 double intPart
= 0.0;
642 bool gotDot
= *aIter
== '.';
645 if (!SVGContentUtils::IsDigit(*aIter
)) {
649 intPart
= 10.0 * intPart
+ SVGContentUtils::DecimalDigitValue(*aIter
);
651 } while (aIter
!= aEnd
&& SVGContentUtils::IsDigit(*aIter
));
654 gotDot
= *aIter
== '.';
658 // Fractional part of the mantissa.
659 double fracPart
= 0.0;
663 if (aIter
== aEnd
|| !SVGContentUtils::IsDigit(*aIter
)) {
667 // Power of ten by which we need to divide the fraction
668 double divisor
= 1.0;
671 fracPart
= 10.0 * fracPart
+ SVGContentUtils::DecimalDigitValue(*aIter
);
674 } while (aIter
!= aEnd
&& SVGContentUtils::IsDigit(*aIter
));
680 int32_t exponent
= 0;
683 if (aIter
!= aEnd
&& (*aIter
== 'e' || *aIter
== 'E')) {
685 RangedPtr
<const char16_t
> expIter(aIter
);
688 if (expIter
!= aEnd
) {
689 expSign
= *expIter
== '-' ? -1 : 1;
690 if (*expIter
== '-' || *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.
703 exponent
= 10.0 * exponent
+ SVGContentUtils::DecimalDigitValue(*aIter
);
705 } while (aIter
!= aEnd
&& SVGContentUtils::IsDigit(*aIter
));
709 // Assemble the number
710 aValue
= sign
* (intPart
+ fracPart
);
712 aValue
*= pow(10.0, expSign
* exponent
);
717 template<class floatType
>
719 SVGContentUtils::ParseNumber(RangedPtr
<const char16_t
>& aIter
,
720 const RangedPtr
<const char16_t
>& aEnd
,
723 RangedPtr
<const char16_t
> iter(aIter
);
726 if (!::ParseNumber(iter
, aEnd
, value
)) {
729 floatType floatValue
= floatType(value
);
730 if (!IsFinite(floatValue
)) {
739 SVGContentUtils::ParseNumber
<float>(RangedPtr
<const char16_t
>& aIter
,
740 const RangedPtr
<const char16_t
>& aEnd
,
744 SVGContentUtils::ParseNumber
<double>(RangedPtr
<const char16_t
>& aIter
,
745 const RangedPtr
<const char16_t
>& aEnd
,
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
>
763 SVGContentUtils::ParseNumber(const nsAString
& aString
,
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
;
773 SVGContentUtils::ParseNumber
<float>(const nsAString
& aString
,
776 SVGContentUtils::ParseNumber
<double>(const nsAString
& aString
,
781 SVGContentUtils::ParseInteger(RangedPtr
<const char16_t
>& aIter
,
782 const RangedPtr
<const char16_t
>& aEnd
,
785 RangedPtr
<const char16_t
> iter(aIter
);
788 if (!ParseOptionalSign(iter
, aEnd
, sign
)) {
792 if (!IsDigit(*iter
)) {
799 if (value
<= std::numeric_limits
<int32_t>::max()) {
800 value
= 10 * value
+ DecimalDigitValue(*iter
);
803 } while (iter
!= aEnd
&& IsDigit(*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())));
814 SVGContentUtils::ParseInteger(const nsAString
& aString
,
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
;
824 SVGContentUtils::CoordToFloat(nsSVGElement
*aContent
,
825 const nsStyleCoord
&aCoord
)
827 switch (aCoord
.GetUnit()) {
828 case eStyleUnit_Factor
:
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
;
844 already_AddRefed
<gfx::Path
>
845 SVGContentUtils::GetPath(const nsAString
& aPathString
)
847 SVGPathData pathData
;
848 nsSVGPathDataParser
parser(aPathString
, &pathData
);
849 if (!parser
.Parse()) {
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);
862 SVGContentUtils::ShapeTypeHasNoCorners(const nsIContent
* aContent
) {
863 return aContent
&& aContent
->IsAnyOfSVGElements(nsGkAtoms::circle
,