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