Line data Source code
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 18 : 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*
26 0 : SVGRectElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
27 : {
28 0 : 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 :
44 11 : SVGRectElement::SVGRectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
45 11 : : SVGRectElementBase(aNodeInfo)
46 : {
47 11 : }
48 :
49 : //----------------------------------------------------------------------
50 : // nsIDOMNode methods
51 :
52 4 : NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGRectElement)
53 :
54 : //----------------------------------------------------------------------
55 :
56 : already_AddRefed<SVGAnimatedLength>
57 0 : SVGRectElement::X()
58 : {
59 0 : return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
60 : }
61 :
62 : already_AddRefed<SVGAnimatedLength>
63 0 : SVGRectElement::Y()
64 : {
65 0 : return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);
66 : }
67 :
68 : already_AddRefed<SVGAnimatedLength>
69 0 : SVGRectElement::Width()
70 : {
71 0 : return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
72 : }
73 :
74 : already_AddRefed<SVGAnimatedLength>
75 0 : SVGRectElement::Height()
76 : {
77 0 : return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
78 : }
79 :
80 : already_AddRefed<SVGAnimatedLength>
81 0 : SVGRectElement::Rx()
82 : {
83 0 : return mLengthAttributes[ATTR_RX].ToDOMAnimatedLength(this);
84 : }
85 :
86 : already_AddRefed<SVGAnimatedLength>
87 0 : SVGRectElement::Ry()
88 : {
89 0 : return mLengthAttributes[ATTR_RY].ToDOMAnimatedLength(this);
90 : }
91 :
92 : //----------------------------------------------------------------------
93 : // nsSVGElement methods
94 :
95 : /* virtual */ bool
96 8 : SVGRectElement::HasValidDimensions() const
97 : {
98 16 : return mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() &&
99 16 : mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0 &&
100 24 : mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() &&
101 16 : mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0;
102 : }
103 :
104 : nsSVGElement::LengthAttributesInfo
105 97 : SVGRectElement::GetLengthInfo()
106 : {
107 : return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
108 97 : ArrayLength(sLengthInfo));
109 : }
110 :
111 : //----------------------------------------------------------------------
112 : // SVGGeometryElement methods
113 :
114 : bool
115 16 : SVGRectElement::GetGeometryBounds(Rect* aBounds,
116 : const StrokeOptions& aStrokeOptions,
117 : const Matrix& aToBoundsSpace,
118 : const Matrix* aToNonScalingStrokeSpace)
119 : {
120 16 : Rect rect;
121 : Float rx, ry;
122 16 : GetAnimatedLengthValues(&rect.x, &rect.y, &rect.width,
123 16 : &rect.height, &rx, &ry, nullptr);
124 :
125 16 : if (rect.IsEmpty()) {
126 : // Rendering of the element disabled
127 0 : rect.SetEmpty(); // Make sure width/height are zero and not negative
128 : // We still want the x/y position from 'rect'
129 0 : *aBounds = aToBoundsSpace.TransformBounds(rect);
130 0 : return true;
131 : }
132 :
133 16 : if (!aToBoundsSpace.IsRectilinear()) {
134 : // We can't ignore the radii in this case if we want tight bounds
135 0 : rx = std::max(rx, 0.0f);
136 0 : ry = std::max(ry, 0.0f);
137 :
138 0 : if (rx != 0 || ry != 0) {
139 0 : return false;
140 : }
141 : }
142 :
143 16 : if (aStrokeOptions.mLineWidth > 0.f) {
144 4 : if (aToNonScalingStrokeSpace) {
145 0 : if (aToNonScalingStrokeSpace->IsRectilinear()) {
146 0 : MOZ_ASSERT(!aToNonScalingStrokeSpace->IsSingular());
147 0 : 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 0 : rect.Inflate(aStrokeOptions.mLineWidth / 2.f);
155 : Matrix nonScalingToBounds =
156 0 : aToNonScalingStrokeSpace->Inverse() * aToBoundsSpace;
157 0 : *aBounds = nonScalingToBounds.TransformBounds(rect);
158 0 : return true;
159 : }
160 0 : return false;
161 : }
162 : // The "beveled" comment above applies here too
163 4 : rect.Inflate(aStrokeOptions.mLineWidth / 2.f);
164 : }
165 :
166 16 : *aBounds = aToBoundsSpace.TransformBounds(rect);
167 16 : return true;
168 : }
169 :
170 : void
171 8 : SVGRectElement::GetAsSimplePath(SimplePath* aSimplePath)
172 : {
173 : float x, y, width, height, rx, ry;
174 8 : GetAnimatedLengthValues(&x, &y, &width, &height, &rx, &ry, nullptr);
175 :
176 8 : if (width <= 0 || height <= 0) {
177 0 : aSimplePath->Reset();
178 0 : return;
179 : }
180 :
181 8 : rx = std::max(rx, 0.0f);
182 8 : ry = std::max(ry, 0.0f);
183 :
184 8 : if (rx != 0 || ry != 0) {
185 4 : aSimplePath->Reset();
186 4 : return;
187 : }
188 :
189 4 : aSimplePath->SetRect(x, y, width, height);
190 : }
191 :
192 : already_AddRefed<Path>
193 4 : SVGRectElement::BuildPath(PathBuilder* aBuilder)
194 : {
195 : float x, y, width, height, rx, ry;
196 4 : GetAnimatedLengthValues(&x, &y, &width, &height, &rx, &ry, nullptr);
197 :
198 4 : if (width <= 0 || height <= 0) {
199 0 : return nullptr;
200 : }
201 :
202 4 : rx = std::max(rx, 0.0f);
203 4 : ry = std::max(ry, 0.0f);
204 :
205 4 : if (rx == 0 && ry == 0) {
206 : // Optimization for the no rounded corners case.
207 0 : Rect r(x, y, width, height);
208 0 : aBuilder->MoveTo(r.TopLeft());
209 0 : aBuilder->LineTo(r.TopRight());
210 0 : aBuilder->LineTo(r.BottomRight());
211 0 : aBuilder->LineTo(r.BottomLeft());
212 0 : 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 4 : bool hasRx = mLengthAttributes[ATTR_RX].IsExplicitlySet();
217 4 : bool hasRy = mLengthAttributes[ATTR_RY].IsExplicitlySet();
218 4 : MOZ_ASSERT(hasRx || hasRy);
219 :
220 4 : if (hasRx && !hasRy) {
221 2 : ry = rx;
222 2 : } else if (hasRy && !hasRx) {
223 0 : rx = ry;
224 : }
225 :
226 : // Clamp rx and ry to half the rect's width and height respectively:
227 4 : rx = std::min(rx, width / 2);
228 4 : ry = std::min(ry, height / 2);
229 :
230 4 : RectCornerRadii radii(rx, ry);
231 4 : AppendRoundedRectToPath(aBuilder, Rect(x, y, width, height), radii);
232 : }
233 :
234 4 : return aBuilder->Finish();
235 : }
236 :
237 : } // namespace dom
238 : } // namespace mozilla
|