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
;
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 //----------------------------------------------------------------------
36 SVGLineElement::SVGLineElement(already_AddRefed
<mozilla::dom::NodeInfo
>& aNodeInfo
)
37 : SVGLineElementBase(aNodeInfo
)
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
) {
52 strokeOptions
.mLineWidth
/ SVG_ZERO_LENGTH_PATH_FIX_FACTOR
;
58 //----------------------------------------------------------------------
61 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGLineElement
)
63 //----------------------------------------------------------------------
65 already_AddRefed
<SVGAnimatedLength
>
68 return mLengthAttributes
[ATTR_X1
].ToDOMAnimatedLength(this);
71 already_AddRefed
<SVGAnimatedLength
>
74 return mLengthAttributes
[ATTR_Y1
].ToDOMAnimatedLength(this);
77 already_AddRefed
<SVGAnimatedLength
>
80 return mLengthAttributes
[ATTR_X2
].ToDOMAnimatedLength(this);
83 already_AddRefed
<SVGAnimatedLength
>
86 return mLengthAttributes
[ATTR_Y2
].ToDOMAnimatedLength(this);
89 //----------------------------------------------------------------------
93 SVGLineElement::IsAttributeMapped(const nsAtom
* name
) const
95 static const MappedAttributeEntry
* const map
[] = {
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
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
));
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();
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
)));
166 // transform from non-scaling-stroke space to the space in which we compute
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.
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
);
189 bounds
.Inflate(aStrokeOptions
.mLineWidth
/ 2.f
);
190 *aBounds
= aToBoundsSpace
.TransformBounds(bounds
);
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
));
215 if (aStrokeOptions
.mLineCap
== CapStyle::BUTT
) {
217 xDelta
= yDelta
= 0.f
;
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
);
228 MOZ_ASSERT(aStrokeOptions
.mLineCap
== CapStyle::SQUARE
);
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
);
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
]));
258 } // namespace mozilla