Bug 1265584 [wpt PR 11167] - [Gecko Bug 1265584] Move wptrunner marionette usage...
[gecko.git] / dom / svg / SVGLineElement.cpp
bloba164cda3c1425acff04e21764f58cab98c1b1244
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 "mozilla/dom/SVGLineElement.h"
8 #include "mozilla/dom/SVGLengthBinding.h"
9 #include "mozilla/dom/SVGLineElementBinding.h"
10 #include "mozilla/gfx/2D.h"
12 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Line)
14 using namespace mozilla::gfx;
16 namespace mozilla {
17 namespace dom {
19 JSObject*
20 SVGLineElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
22 return SVGLineElement_Binding::Wrap(aCx, this, aGivenProto);
25 nsSVGElement::LengthInfo SVGLineElement::sLengthInfo[4] =
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 },
33 //----------------------------------------------------------------------
34 // Implementation
36 SVGLineElement::SVGLineElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
37 : SVGLineElementBase(aNodeInfo)
41 void
42 SVGLineElement::MaybeAdjustForZeroLength(float aX1, float aY1,
43 float& aX2, float aY2)
45 if (aX1 == aX2 && aY1 == aY2) {
46 SVGContentUtils::AutoStrokeOptions strokeOptions;
47 SVGContentUtils::GetStrokeOptions(&strokeOptions, this, nullptr, nullptr,
48 SVGContentUtils::eIgnoreStrokeDashing);
50 if (strokeOptions.mLineCap != CapStyle::BUTT) {
51 float tinyLength =
52 strokeOptions.mLineWidth / SVG_ZERO_LENGTH_PATH_FIX_FACTOR;
53 aX2 += tinyLength;
58 //----------------------------------------------------------------------
59 // nsINode methods
61 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGLineElement)
63 //----------------------------------------------------------------------
65 already_AddRefed<SVGAnimatedLength>
66 SVGLineElement::X1()
68 return mLengthAttributes[ATTR_X1].ToDOMAnimatedLength(this);
71 already_AddRefed<SVGAnimatedLength>
72 SVGLineElement::Y1()
74 return mLengthAttributes[ATTR_Y1].ToDOMAnimatedLength(this);
77 already_AddRefed<SVGAnimatedLength>
78 SVGLineElement::X2()
80 return mLengthAttributes[ATTR_X2].ToDOMAnimatedLength(this);
83 already_AddRefed<SVGAnimatedLength>
84 SVGLineElement::Y2()
86 return mLengthAttributes[ATTR_Y2].ToDOMAnimatedLength(this);
89 //----------------------------------------------------------------------
90 // nsIContent methods
92 NS_IMETHODIMP_(bool)
93 SVGLineElement::IsAttributeMapped(const nsAtom* name) const
95 static const MappedAttributeEntry* const map[] = {
96 sMarkersMap
99 return FindAttributeDependence(name, map) ||
100 SVGLineElementBase::IsAttributeMapped(name);
103 //----------------------------------------------------------------------
104 // nsSVGElement methods
106 nsSVGElement::LengthAttributesInfo
107 SVGLineElement::GetLengthInfo()
109 return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
110 ArrayLength(sLengthInfo));
113 //----------------------------------------------------------------------
114 // SVGGeometryElement methods
116 void
117 SVGLineElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks) {
118 float x1, y1, x2, y2;
120 GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr);
122 float angle = atan2(y2 - y1, x2 - x1);
124 aMarks->AppendElement(nsSVGMark(x1, y1, angle, nsSVGMark::eStart));
125 aMarks->AppendElement(nsSVGMark(x2, y2, angle, nsSVGMark::eEnd));
128 void
129 SVGLineElement::GetAsSimplePath(SimplePath* aSimplePath)
131 float x1, y1, x2, y2;
132 GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr);
134 MaybeAdjustForZeroLength(x1, y1, x2, y2);
135 aSimplePath->SetLine(x1, y1, x2, y2);
138 already_AddRefed<Path>
139 SVGLineElement::BuildPath(PathBuilder* aBuilder)
141 float x1, y1, x2, y2;
142 GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr);
144 MaybeAdjustForZeroLength(x1, y1, x2, y2);
145 aBuilder->MoveTo(Point(x1, y1));
146 aBuilder->LineTo(Point(x2, y2));
148 return aBuilder->Finish();
151 bool
152 SVGLineElement::GetGeometryBounds(Rect* aBounds,
153 const StrokeOptions& aStrokeOptions,
154 const Matrix& aToBoundsSpace,
155 const Matrix* aToNonScalingStrokeSpace)
157 float x1, y1, x2, y2;
158 GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr);
160 if (aStrokeOptions.mLineWidth <= 0) {
161 *aBounds = Rect(aToBoundsSpace.TransformPoint(Point(x1, y1)), Size());
162 aBounds->ExpandToEnclose(aToBoundsSpace.TransformPoint(Point(x2, y2)));
163 return true;
166 // transform from non-scaling-stroke space to the space in which we compute
167 // bounds
168 Matrix nonScalingToBounds;
169 if (aToNonScalingStrokeSpace) {
170 MOZ_ASSERT(!aToNonScalingStrokeSpace->IsSingular());
171 Matrix nonScalingToUser = aToNonScalingStrokeSpace->Inverse();
172 nonScalingToBounds = nonScalingToUser * aToBoundsSpace;
175 if (aStrokeOptions.mLineCap == CapStyle::ROUND) {
176 if (!aToBoundsSpace.IsRectilinear() ||
177 (aToNonScalingStrokeSpace &&
178 !aToNonScalingStrokeSpace->IsRectilinear())) {
179 // TODO: handle this case.
180 return false;
182 Rect bounds(Point(x1, y1), Size());
183 bounds.ExpandToEnclose(Point(x2, y2));
184 if (aToNonScalingStrokeSpace) {
185 bounds = aToNonScalingStrokeSpace->TransformBounds(bounds);
186 bounds.Inflate(aStrokeOptions.mLineWidth / 2.f);
187 *aBounds = nonScalingToBounds.TransformBounds(bounds);
188 } else {
189 bounds.Inflate(aStrokeOptions.mLineWidth / 2.f);
190 *aBounds = aToBoundsSpace.TransformBounds(bounds);
192 return true;
195 // Handle butt and square linecap, normal and non-scaling stroke cases
196 // together: start with endpoints (x1, y1), (x2, y2) in the stroke space,
197 // compute the four corners of the stroked line, transform the corners to
198 // bounds space, and compute bounds there.
200 if (aToNonScalingStrokeSpace) {
201 Point nonScalingSpaceP1, nonScalingSpaceP2;
202 nonScalingSpaceP1 = aToNonScalingStrokeSpace->TransformPoint(Point(x1, y1));
203 nonScalingSpaceP2 = aToNonScalingStrokeSpace->TransformPoint(Point(x2, y2));
204 x1 = nonScalingSpaceP1.x;
205 y1 = nonScalingSpaceP1.y;
206 x2 = nonScalingSpaceP2.x;
207 y2 = nonScalingSpaceP2.y;
210 Float length = Float(NS_hypot(x2 - x1, y2 - y1));
211 Float xDelta;
212 Float yDelta;
213 Point points[4];
215 if (aStrokeOptions.mLineCap == CapStyle::BUTT) {
216 if (length == 0.f) {
217 xDelta = yDelta = 0.f;
218 } else {
219 Float ratio = aStrokeOptions.mLineWidth / 2.f / length;
220 xDelta = ratio * (y2 - y1);
221 yDelta = ratio * (x2 - x1);
223 points[0] = Point(x1 - xDelta, y1 + yDelta);
224 points[1] = Point(x1 + xDelta, y1 - yDelta);
225 points[2] = Point(x2 + xDelta, y2 - yDelta);
226 points[3] = Point(x2 - xDelta, y2 + yDelta);
227 } else {
228 MOZ_ASSERT(aStrokeOptions.mLineCap == CapStyle::SQUARE);
229 if (length == 0.f) {
230 xDelta = yDelta = aStrokeOptions.mLineWidth / 2.f;
231 points[0] = Point(x1 - xDelta, y1 + yDelta);
232 points[1] = Point(x1 - xDelta, y1 - yDelta);
233 points[2] = Point(x1 + xDelta, y1 - yDelta);
234 points[3] = Point(x1 + xDelta, y1 + yDelta);
235 } else {
236 Float ratio = aStrokeOptions.mLineWidth / 2.f / length;
237 yDelta = ratio * (x2 - x1);
238 xDelta = ratio * (y2 - y1);
239 points[0] = Point(x1 - yDelta - xDelta, y1 - xDelta + yDelta);
240 points[1] = Point(x1 - yDelta + xDelta, y1 - xDelta - yDelta);
241 points[2] = Point(x2 + yDelta + xDelta, y2 + xDelta - yDelta);
242 points[3] = Point(x2 + yDelta - xDelta, y2 + xDelta + yDelta);
246 const Matrix& toBoundsSpace = aToNonScalingStrokeSpace ?
247 nonScalingToBounds : aToBoundsSpace;
249 *aBounds = Rect(toBoundsSpace.TransformPoint(points[0]), Size());
250 for (uint32_t i = 1; i < 4; ++i) {
251 aBounds->ExpandToEnclose(toBoundsSpace.TransformPoint(points[i]));
254 return true;
257 } // namespace dom
258 } // namespace mozilla