/src/mozilla-central/dom/svg/SVGLineElement.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/SVGLineElement.h" |
8 | | #include "mozilla/dom/SVGLengthBinding.h" |
9 | | #include "mozilla/dom/SVGLineElementBinding.h" |
10 | | #include "mozilla/gfx/2D.h" |
11 | | |
12 | | NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Line) |
13 | | |
14 | | using namespace mozilla::gfx; |
15 | | |
16 | | namespace mozilla { |
17 | | namespace dom { |
18 | | |
19 | | JSObject* |
20 | | SVGLineElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) |
21 | 0 | { |
22 | 0 | return SVGLineElement_Binding::Wrap(aCx, this, aGivenProto); |
23 | 0 | } |
24 | | |
25 | | nsSVGElement::LengthInfo SVGLineElement::sLengthInfo[4] = |
26 | | { |
27 | | { &nsGkAtoms::x1, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, |
28 | | { &nsGkAtoms::y1, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, |
29 | | { &nsGkAtoms::x2, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X }, |
30 | | { &nsGkAtoms::y2, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y }, |
31 | | }; |
32 | | |
33 | | //---------------------------------------------------------------------- |
34 | | // Implementation |
35 | | |
36 | | SVGLineElement::SVGLineElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) |
37 | | : SVGLineElementBase(std::move(aNodeInfo)) |
38 | 0 | { |
39 | 0 | } |
40 | | |
41 | | void |
42 | | SVGLineElement::MaybeAdjustForZeroLength(float aX1, float aY1, |
43 | | float& aX2, float aY2) |
44 | 0 | { |
45 | 0 | if (aX1 == aX2 && aY1 == aY2) { |
46 | 0 | SVGContentUtils::AutoStrokeOptions strokeOptions; |
47 | 0 | SVGContentUtils::GetStrokeOptions(&strokeOptions, this, nullptr, nullptr, |
48 | 0 | SVGContentUtils::eIgnoreStrokeDashing); |
49 | 0 |
|
50 | 0 | if (strokeOptions.mLineCap != CapStyle::BUTT) { |
51 | 0 | float tinyLength = |
52 | 0 | strokeOptions.mLineWidth / SVG_ZERO_LENGTH_PATH_FIX_FACTOR; |
53 | 0 | aX2 += tinyLength; |
54 | 0 | } |
55 | 0 | } |
56 | 0 | } |
57 | | |
58 | | //---------------------------------------------------------------------- |
59 | | // nsINode methods |
60 | | |
61 | | NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGLineElement) |
62 | | |
63 | | //---------------------------------------------------------------------- |
64 | | |
65 | | already_AddRefed<SVGAnimatedLength> |
66 | | SVGLineElement::X1() |
67 | 0 | { |
68 | 0 | return mLengthAttributes[ATTR_X1].ToDOMAnimatedLength(this); |
69 | 0 | } |
70 | | |
71 | | already_AddRefed<SVGAnimatedLength> |
72 | | SVGLineElement::Y1() |
73 | 0 | { |
74 | 0 | return mLengthAttributes[ATTR_Y1].ToDOMAnimatedLength(this); |
75 | 0 | } |
76 | | |
77 | | already_AddRefed<SVGAnimatedLength> |
78 | | SVGLineElement::X2() |
79 | 0 | { |
80 | 0 | return mLengthAttributes[ATTR_X2].ToDOMAnimatedLength(this); |
81 | 0 | } |
82 | | |
83 | | already_AddRefed<SVGAnimatedLength> |
84 | | SVGLineElement::Y2() |
85 | 0 | { |
86 | 0 | return mLengthAttributes[ATTR_Y2].ToDOMAnimatedLength(this); |
87 | 0 | } |
88 | | |
89 | | //---------------------------------------------------------------------- |
90 | | // nsIContent methods |
91 | | |
92 | | NS_IMETHODIMP_(bool) |
93 | | SVGLineElement::IsAttributeMapped(const nsAtom* name) const |
94 | 0 | { |
95 | 0 | static const MappedAttributeEntry* const map[] = { |
96 | 0 | sMarkersMap |
97 | 0 | }; |
98 | 0 |
|
99 | 0 | return FindAttributeDependence(name, map) || |
100 | 0 | SVGLineElementBase::IsAttributeMapped(name); |
101 | 0 | } |
102 | | |
103 | | //---------------------------------------------------------------------- |
104 | | // nsSVGElement methods |
105 | | |
106 | | nsSVGElement::LengthAttributesInfo |
107 | | SVGLineElement::GetLengthInfo() |
108 | 0 | { |
109 | 0 | return LengthAttributesInfo(mLengthAttributes, sLengthInfo, |
110 | 0 | ArrayLength(sLengthInfo)); |
111 | 0 | } |
112 | | |
113 | | //---------------------------------------------------------------------- |
114 | | // SVGGeometryElement methods |
115 | | |
116 | | void |
117 | 0 | SVGLineElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks) { |
118 | 0 | float x1, y1, x2, y2; |
119 | 0 |
|
120 | 0 | GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr); |
121 | 0 |
|
122 | 0 | float angle = atan2(y2 - y1, x2 - x1); |
123 | 0 |
|
124 | 0 | aMarks->AppendElement(nsSVGMark(x1, y1, angle, nsSVGMark::eStart)); |
125 | 0 | aMarks->AppendElement(nsSVGMark(x2, y2, angle, nsSVGMark::eEnd)); |
126 | 0 | } |
127 | | |
128 | | void |
129 | | SVGLineElement::GetAsSimplePath(SimplePath* aSimplePath) |
130 | 0 | { |
131 | 0 | float x1, y1, x2, y2; |
132 | 0 | GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr); |
133 | 0 |
|
134 | 0 | MaybeAdjustForZeroLength(x1, y1, x2, y2); |
135 | 0 | aSimplePath->SetLine(x1, y1, x2, y2); |
136 | 0 | } |
137 | | |
138 | | already_AddRefed<Path> |
139 | | SVGLineElement::BuildPath(PathBuilder* aBuilder) |
140 | 0 | { |
141 | 0 | float x1, y1, x2, y2; |
142 | 0 | GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr); |
143 | 0 |
|
144 | 0 | MaybeAdjustForZeroLength(x1, y1, x2, y2); |
145 | 0 | aBuilder->MoveTo(Point(x1, y1)); |
146 | 0 | aBuilder->LineTo(Point(x2, y2)); |
147 | 0 |
|
148 | 0 | return aBuilder->Finish(); |
149 | 0 | } |
150 | | |
151 | | bool |
152 | | SVGLineElement::GetGeometryBounds(Rect* aBounds, |
153 | | const StrokeOptions& aStrokeOptions, |
154 | | const Matrix& aToBoundsSpace, |
155 | | const Matrix* aToNonScalingStrokeSpace) |
156 | 0 | { |
157 | 0 | float x1, y1, x2, y2; |
158 | 0 | GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr); |
159 | 0 |
|
160 | 0 | if (aStrokeOptions.mLineWidth <= 0) { |
161 | 0 | *aBounds = Rect(aToBoundsSpace.TransformPoint(Point(x1, y1)), Size()); |
162 | 0 | aBounds->ExpandToEnclose(aToBoundsSpace.TransformPoint(Point(x2, y2))); |
163 | 0 | return true; |
164 | 0 | } |
165 | 0 | |
166 | 0 | // transform from non-scaling-stroke space to the space in which we compute |
167 | 0 | // bounds |
168 | 0 | Matrix nonScalingToBounds; |
169 | 0 | if (aToNonScalingStrokeSpace) { |
170 | 0 | MOZ_ASSERT(!aToNonScalingStrokeSpace->IsSingular()); |
171 | 0 | Matrix nonScalingToUser = aToNonScalingStrokeSpace->Inverse(); |
172 | 0 | nonScalingToBounds = nonScalingToUser * aToBoundsSpace; |
173 | 0 | } |
174 | 0 |
|
175 | 0 | if (aStrokeOptions.mLineCap == CapStyle::ROUND) { |
176 | 0 | if (!aToBoundsSpace.IsRectilinear() || |
177 | 0 | (aToNonScalingStrokeSpace && |
178 | 0 | !aToNonScalingStrokeSpace->IsRectilinear())) { |
179 | 0 | // TODO: handle this case. |
180 | 0 | return false; |
181 | 0 | } |
182 | 0 | Rect bounds(Point(x1, y1), Size()); |
183 | 0 | bounds.ExpandToEnclose(Point(x2, y2)); |
184 | 0 | if (aToNonScalingStrokeSpace) { |
185 | 0 | bounds = aToNonScalingStrokeSpace->TransformBounds(bounds); |
186 | 0 | bounds.Inflate(aStrokeOptions.mLineWidth / 2.f); |
187 | 0 | *aBounds = nonScalingToBounds.TransformBounds(bounds); |
188 | 0 | } else { |
189 | 0 | bounds.Inflate(aStrokeOptions.mLineWidth / 2.f); |
190 | 0 | *aBounds = aToBoundsSpace.TransformBounds(bounds); |
191 | 0 | } |
192 | 0 | return true; |
193 | 0 | } |
194 | 0 |
|
195 | 0 | // Handle butt and square linecap, normal and non-scaling stroke cases |
196 | 0 | // together: start with endpoints (x1, y1), (x2, y2) in the stroke space, |
197 | 0 | // compute the four corners of the stroked line, transform the corners to |
198 | 0 | // bounds space, and compute bounds there. |
199 | 0 |
|
200 | 0 | if (aToNonScalingStrokeSpace) { |
201 | 0 | Point nonScalingSpaceP1, nonScalingSpaceP2; |
202 | 0 | nonScalingSpaceP1 = aToNonScalingStrokeSpace->TransformPoint(Point(x1, y1)); |
203 | 0 | nonScalingSpaceP2 = aToNonScalingStrokeSpace->TransformPoint(Point(x2, y2)); |
204 | 0 | x1 = nonScalingSpaceP1.x; |
205 | 0 | y1 = nonScalingSpaceP1.y; |
206 | 0 | x2 = nonScalingSpaceP2.x; |
207 | 0 | y2 = nonScalingSpaceP2.y; |
208 | 0 | } |
209 | 0 |
|
210 | 0 | Float length = Float(NS_hypot(x2 - x1, y2 - y1)); |
211 | 0 | Float xDelta; |
212 | 0 | Float yDelta; |
213 | 0 | Point points[4]; |
214 | 0 |
|
215 | 0 | if (aStrokeOptions.mLineCap == CapStyle::BUTT) { |
216 | 0 | if (length == 0.f) { |
217 | 0 | xDelta = yDelta = 0.f; |
218 | 0 | } else { |
219 | 0 | Float ratio = aStrokeOptions.mLineWidth / 2.f / length; |
220 | 0 | xDelta = ratio * (y2 - y1); |
221 | 0 | yDelta = ratio * (x2 - x1); |
222 | 0 | } |
223 | 0 | points[0] = Point(x1 - xDelta, y1 + yDelta); |
224 | 0 | points[1] = Point(x1 + xDelta, y1 - yDelta); |
225 | 0 | points[2] = Point(x2 + xDelta, y2 - yDelta); |
226 | 0 | points[3] = Point(x2 - xDelta, y2 + yDelta); |
227 | 0 | } else { |
228 | 0 | MOZ_ASSERT(aStrokeOptions.mLineCap == CapStyle::SQUARE); |
229 | 0 | if (length == 0.f) { |
230 | 0 | xDelta = yDelta = aStrokeOptions.mLineWidth / 2.f; |
231 | 0 | points[0] = Point(x1 - xDelta, y1 + yDelta); |
232 | 0 | points[1] = Point(x1 - xDelta, y1 - yDelta); |
233 | 0 | points[2] = Point(x1 + xDelta, y1 - yDelta); |
234 | 0 | points[3] = Point(x1 + xDelta, y1 + yDelta); |
235 | 0 | } else { |
236 | 0 | Float ratio = aStrokeOptions.mLineWidth / 2.f / length; |
237 | 0 | yDelta = ratio * (x2 - x1); |
238 | 0 | xDelta = ratio * (y2 - y1); |
239 | 0 | points[0] = Point(x1 - yDelta - xDelta, y1 - xDelta + yDelta); |
240 | 0 | points[1] = Point(x1 - yDelta + xDelta, y1 - xDelta - yDelta); |
241 | 0 | points[2] = Point(x2 + yDelta + xDelta, y2 + xDelta - yDelta); |
242 | 0 | points[3] = Point(x2 + yDelta - xDelta, y2 + xDelta + yDelta); |
243 | 0 | } |
244 | 0 | } |
245 | 0 |
|
246 | 0 | const Matrix& toBoundsSpace = aToNonScalingStrokeSpace ? |
247 | 0 | nonScalingToBounds : aToBoundsSpace; |
248 | 0 |
|
249 | 0 | *aBounds = Rect(toBoundsSpace.TransformPoint(points[0]), Size()); |
250 | 0 | for (uint32_t i = 1; i < 4; ++i) { |
251 | 0 | aBounds->ExpandToEnclose(toBoundsSpace.TransformPoint(points[i])); |
252 | 0 | } |
253 | 0 |
|
254 | 0 | return true; |
255 | 0 | } |
256 | | |
257 | | } // namespace dom |
258 | | } // namespace mozilla |