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 #include "ComputedStyle.h"
8 #include "mozilla/dom/SVGEllipseElement.h"
9 #include "mozilla/dom/SVGEllipseElementBinding.h"
10 #include "mozilla/dom/SVGLengthBinding.h"
11 #include "mozilla/gfx/2D.h"
12 #include "mozilla/gfx/PathHelpers.h"
13 #include "mozilla/RefPtr.h"
14 #include "SVGGeometryProperty.h"
16 NS_IMPL_NS_NEW_SVG_ELEMENT(Ellipse
)
18 using namespace mozilla::gfx
;
20 namespace mozilla::dom
{
22 JSObject
* SVGEllipseElement::WrapNode(JSContext
* aCx
,
23 JS::Handle
<JSObject
*> aGivenProto
) {
24 return SVGEllipseElement_Binding::Wrap(aCx
, this, aGivenProto
);
27 SVGElement::LengthInfo
SVGEllipseElement::sLengthInfo
[4] = {
28 {nsGkAtoms::cx
, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
,
30 {nsGkAtoms::cy
, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
,
32 {nsGkAtoms::rx
, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
,
34 {nsGkAtoms::ry
, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
,
38 //----------------------------------------------------------------------
41 SVGEllipseElement::SVGEllipseElement(
42 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
43 : SVGEllipseElementBase(std::move(aNodeInfo
)) {}
45 bool SVGEllipseElement::IsAttributeMapped(const nsAtom
* aAttribute
) const {
46 return IsInLengthInfo(aAttribute
, sLengthInfo
) ||
47 SVGEllipseElementBase::IsAttributeMapped(aAttribute
);
50 namespace SVGT
= SVGGeometryProperty::Tags
;
52 //----------------------------------------------------------------------
55 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGEllipseElement
)
57 //----------------------------------------------------------------------
58 // nsIDOMSVGEllipseElement methods
60 already_AddRefed
<DOMSVGAnimatedLength
> SVGEllipseElement::Cx() {
61 return mLengthAttributes
[CX
].ToDOMAnimatedLength(this);
64 already_AddRefed
<DOMSVGAnimatedLength
> SVGEllipseElement::Cy() {
65 return mLengthAttributes
[CY
].ToDOMAnimatedLength(this);
68 already_AddRefed
<DOMSVGAnimatedLength
> SVGEllipseElement::Rx() {
69 return mLengthAttributes
[RX
].ToDOMAnimatedLength(this);
72 already_AddRefed
<DOMSVGAnimatedLength
> SVGEllipseElement::Ry() {
73 return mLengthAttributes
[RY
].ToDOMAnimatedLength(this);
76 //----------------------------------------------------------------------
80 bool SVGEllipseElement::HasValidDimensions() const {
83 if (SVGGeometryProperty::ResolveAll
<SVGT::Rx
, SVGT::Ry
>(this, &rx
, &ry
)) {
84 return rx
> 0 && ry
> 0;
86 // This function might be called for an element in display:none subtree
87 // (e.g. SMIL animateMotion), we fall back to use SVG attributes.
88 bool hasRx
= mLengthAttributes
[RX
].IsExplicitlySet();
89 bool hasRy
= mLengthAttributes
[RY
].IsExplicitlySet();
90 if ((hasRx
&& mLengthAttributes
[RX
].GetAnimValInSpecifiedUnits() <= 0) ||
91 (hasRy
&& mLengthAttributes
[RY
].GetAnimValInSpecifiedUnits() <= 0)) {
94 return hasRx
|| hasRy
;
97 SVGElement::LengthAttributesInfo
SVGEllipseElement::GetLengthInfo() {
98 return LengthAttributesInfo(mLengthAttributes
, sLengthInfo
,
99 ArrayLength(sLengthInfo
));
102 //----------------------------------------------------------------------
103 // SVGGeometryElement methods
105 bool SVGEllipseElement::GetGeometryBounds(
106 Rect
* aBounds
, const StrokeOptions
& aStrokeOptions
,
107 const Matrix
& aToBoundsSpace
, const Matrix
* aToNonScalingStrokeSpace
) {
111 SVGGeometryProperty::ResolveAll
<SVGT::Cx
, SVGT::Cy
, SVGT::Rx
, SVGT::Ry
>(
112 this, &x
, &y
, &rx
, &ry
);
113 MOZ_ASSERT(ok
, "SVGGeometryProperty::ResolveAll failed");
115 if (rx
<= 0.f
|| ry
<= 0.f
) {
116 // Rendering of the element is disabled
117 *aBounds
= Rect(aToBoundsSpace
.TransformPoint(Point(x
, y
)), Size());
121 if (aToBoundsSpace
.IsRectilinear()) {
122 // Optimize the case where we can treat the ellipse as a rectangle and
123 // still get tight bounds.
124 if (aStrokeOptions
.mLineWidth
> 0.f
) {
125 if (aToNonScalingStrokeSpace
) {
126 if (aToNonScalingStrokeSpace
->IsRectilinear()) {
127 MOZ_ASSERT(!aToNonScalingStrokeSpace
->IsSingular());
128 Rect
userBounds(x
- rx
, y
- ry
, 2 * rx
, 2 * ry
);
129 SVGContentUtils::RectilinearGetStrokeBounds(
130 userBounds
, aToBoundsSpace
, *aToNonScalingStrokeSpace
,
131 aStrokeOptions
.mLineWidth
, aBounds
);
136 rx
+= aStrokeOptions
.mLineWidth
/ 2.f
;
137 ry
+= aStrokeOptions
.mLineWidth
/ 2.f
;
139 Rect
rect(x
- rx
, y
- ry
, 2 * rx
, 2 * ry
);
140 *aBounds
= aToBoundsSpace
.TransformBounds(rect
);
147 already_AddRefed
<Path
> SVGEllipseElement::BuildPath(PathBuilder
* aBuilder
) {
150 if (!SVGGeometryProperty::ResolveAll
<SVGT::Cx
, SVGT::Cy
, SVGT::Rx
, SVGT::Ry
>(
151 this, &x
, &y
, &rx
, &ry
)) {
152 // This function might be called for element in display:none subtree
153 // (e.g. getTotalLength), we fall back to use SVG attributes.
154 GetAnimatedLengthValues(&x
, &y
, &rx
, &ry
, nullptr);
157 if (rx
<= 0.0f
|| ry
<= 0.0f
) {
161 EllipseToBezier(aBuilder
, Point(x
, y
), Size(rx
, ry
));
163 return aBuilder
->Finish();
166 bool SVGEllipseElement::IsLengthChangedViaCSS(const ComputedStyle
& aNewStyle
,
167 const ComputedStyle
& aOldStyle
) {
168 const auto& newSVGReset
= *aNewStyle
.StyleSVGReset();
169 const auto& oldSVGReset
= *aOldStyle
.StyleSVGReset();
170 return newSVGReset
.mCx
!= oldSVGReset
.mCx
||
171 newSVGReset
.mCy
!= oldSVGReset
.mCy
||
172 newSVGReset
.mRx
!= oldSVGReset
.mRx
||
173 newSVGReset
.mRy
!= oldSVGReset
.mRy
;
176 nsCSSPropertyID
SVGEllipseElement::GetCSSPropertyIdForAttrEnum(
180 return eCSSProperty_cx
;
182 return eCSSProperty_cy
;
184 return eCSSProperty_rx
;
186 return eCSSProperty_ry
;
188 MOZ_ASSERT_UNREACHABLE("Unknown attr enum");
189 return eCSSProperty_UNKNOWN
;
193 } // namespace mozilla::dom