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 #include "mozilla/dom/SVGRectElement.h"
8 #include "nsGkAtoms.h"
9 #include "mozilla/dom/SVGRectElementBinding.h"
10 #include "mozilla/gfx/2D.h"
11 #include "mozilla/gfx/Matrix.h"
12 #include "mozilla/gfx/Rect.h"
13 #include "mozilla/gfx/PathHelpers.h"
14 #include <algorithm>
15 
16 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Rect)
17 
18 using namespace mozilla::gfx;
19 
20 namespace mozilla {
21 namespace dom {
22 
23 class SVGAnimatedLength;
24 
25 JSObject*
WrapNode(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)26 SVGRectElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
27 {
28   return SVGRectElementBinding::Wrap(aCx, this, aGivenProto);
29 }
30 
31 nsSVGElement::LengthInfo SVGRectElement::sLengthInfo[6] =
32 {
33   { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
34   { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y },
35   { &nsGkAtoms::width, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
36   { &nsGkAtoms::height, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y },
37   { &nsGkAtoms::rx, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
38   { &nsGkAtoms::ry, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }
39 };
40 
41 //----------------------------------------------------------------------
42 // Implementation
43 
SVGRectElement(already_AddRefed<mozilla::dom::NodeInfo> & aNodeInfo)44 SVGRectElement::SVGRectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
45   : SVGRectElementBase(aNodeInfo)
46 {
47 }
48 
49 //----------------------------------------------------------------------
50 // nsIDOMNode methods
51 
NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGRectElement)52 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGRectElement)
53 
54 //----------------------------------------------------------------------
55 
56 already_AddRefed<SVGAnimatedLength>
57 SVGRectElement::X()
58 {
59   return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
60 }
61 
62 already_AddRefed<SVGAnimatedLength>
Y()63 SVGRectElement::Y()
64 {
65   return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);
66 }
67 
68 already_AddRefed<SVGAnimatedLength>
Width()69 SVGRectElement::Width()
70 {
71   return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
72 }
73 
74 already_AddRefed<SVGAnimatedLength>
Height()75 SVGRectElement::Height()
76 {
77   return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
78 }
79 
80 already_AddRefed<SVGAnimatedLength>
Rx()81 SVGRectElement::Rx()
82 {
83   return mLengthAttributes[ATTR_RX].ToDOMAnimatedLength(this);
84 }
85 
86 already_AddRefed<SVGAnimatedLength>
Ry()87 SVGRectElement::Ry()
88 {
89   return mLengthAttributes[ATTR_RY].ToDOMAnimatedLength(this);
90 }
91 
92 //----------------------------------------------------------------------
93 // nsSVGElement methods
94 
95 /* virtual */ bool
HasValidDimensions() const96 SVGRectElement::HasValidDimensions() const
97 {
98   return mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() &&
99          mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0 &&
100          mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() &&
101          mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0;
102 }
103 
104 nsSVGElement::LengthAttributesInfo
GetLengthInfo()105 SVGRectElement::GetLengthInfo()
106 {
107   return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
108                               ArrayLength(sLengthInfo));
109 }
110 
111 //----------------------------------------------------------------------
112 // nsSVGPathGeometryElement methods
113 
114 bool
GetGeometryBounds(Rect * aBounds,const StrokeOptions & aStrokeOptions,const Matrix & aToBoundsSpace,const Matrix * aToNonScalingStrokeSpace)115 SVGRectElement::GetGeometryBounds(Rect* aBounds,
116                                   const StrokeOptions& aStrokeOptions,
117                                   const Matrix& aToBoundsSpace,
118                                   const Matrix* aToNonScalingStrokeSpace)
119 {
120   Rect rect;
121   Float rx, ry;
122   GetAnimatedLengthValues(&rect.x, &rect.y, &rect.width,
123                           &rect.height, &rx, &ry, nullptr);
124 
125   if (rect.IsEmpty()) {
126     // Rendering of the element disabled
127     rect.SetEmpty(); // Make sure width/height are zero and not negative
128     // We still want the x/y position from 'rect'
129     *aBounds = aToBoundsSpace.TransformBounds(rect);
130     return true;
131   }
132 
133   if (!aToBoundsSpace.IsRectilinear()) {
134     // We can't ignore the radii in this case if we want tight bounds
135     rx = std::max(rx, 0.0f);
136     ry = std::max(ry, 0.0f);
137 
138     if (rx != 0 || ry != 0) {
139       return false;
140     }
141   }
142 
143   if (aStrokeOptions.mLineWidth > 0.f) {
144     if (aToNonScalingStrokeSpace) {
145       if (aToNonScalingStrokeSpace->IsRectilinear()) {
146         MOZ_ASSERT(!aToNonScalingStrokeSpace->IsSingular());
147         rect = aToNonScalingStrokeSpace->TransformBounds(rect);
148         // Note that, in principle, an author could cause the corners of the
149         // rect to be beveled by specifying stroke-linejoin or setting
150         // stroke-miterlimit to be less than sqrt(2). In that very unlikely
151         // event the bounds that we calculate here may be too big if
152         // aToBoundsSpace is non-rectilinear. This is likely to be so rare it's
153         // not worth handling though.
154         rect.Inflate(aStrokeOptions.mLineWidth / 2.f);
155         Matrix nonScalingToBounds =
156           aToNonScalingStrokeSpace->Inverse() * aToBoundsSpace;
157         *aBounds = nonScalingToBounds.TransformBounds(rect);
158         return true;
159       }
160       return false;
161     }
162     // The "beveled" comment above applies here too
163     rect.Inflate(aStrokeOptions.mLineWidth / 2.f);
164   }
165 
166   *aBounds = aToBoundsSpace.TransformBounds(rect);
167   return true;
168 }
169 
170 void
GetAsSimplePath(SimplePath * aSimplePath)171 SVGRectElement::GetAsSimplePath(SimplePath* aSimplePath)
172 {
173   float x, y, width, height, rx, ry;
174   GetAnimatedLengthValues(&x, &y, &width, &height, &rx, &ry, nullptr);
175 
176   if (width <= 0 || height <= 0) {
177     aSimplePath->Reset();
178     return;
179   }
180 
181   rx = std::max(rx, 0.0f);
182   ry = std::max(ry, 0.0f);
183 
184   if (rx != 0 || ry != 0) {
185     aSimplePath->Reset();
186     return;
187   }
188 
189   aSimplePath->SetRect(x, y, width, height);
190 }
191 
192 already_AddRefed<Path>
BuildPath(PathBuilder * aBuilder)193 SVGRectElement::BuildPath(PathBuilder* aBuilder)
194 {
195   float x, y, width, height, rx, ry;
196   GetAnimatedLengthValues(&x, &y, &width, &height, &rx, &ry, nullptr);
197 
198   if (width <= 0 || height <= 0) {
199     return nullptr;
200   }
201 
202   rx = std::max(rx, 0.0f);
203   ry = std::max(ry, 0.0f);
204 
205   if (rx == 0 && ry == 0) {
206     // Optimization for the no rounded corners case.
207     Rect r(x, y, width, height);
208     aBuilder->MoveTo(r.TopLeft());
209     aBuilder->LineTo(r.TopRight());
210     aBuilder->LineTo(r.BottomRight());
211     aBuilder->LineTo(r.BottomLeft());
212     aBuilder->Close();
213   } else {
214     // If either the 'rx' or the 'ry' attribute isn't set, then we have to
215     // set it to the value of the other:
216     bool hasRx = mLengthAttributes[ATTR_RX].IsExplicitlySet();
217     bool hasRy = mLengthAttributes[ATTR_RY].IsExplicitlySet();
218     MOZ_ASSERT(hasRx || hasRy);
219 
220     if (hasRx && !hasRy) {
221       ry = rx;
222     } else if (hasRy && !hasRx) {
223       rx = ry;
224     }
225 
226     // Clamp rx and ry to half the rect's width and height respectively:
227     rx = std::min(rx, width / 2);
228     ry = std::min(ry, height / 2);
229 
230     RectCornerRadii radii(rx, ry);
231     AppendRoundedRectToPath(aBuilder, Rect(x, y, width, height), radii);
232   }
233 
234   return aBuilder->Finish();
235 }
236 
237 } // namespace dom
238 } // namespace mozilla
239