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_SVG_ELEMENT(Line
)
14 using namespace mozilla::gfx
;
16 namespace mozilla::dom
{
18 JSObject
* SVGLineElement::WrapNode(JSContext
* aCx
,
19 JS::Handle
<JSObject
*> aGivenProto
) {
20 return SVGLineElement_Binding::Wrap(aCx
, this, aGivenProto
);
23 SVGElement::LengthInfo
SVGLineElement::sLengthInfo
[4] = {
24 {nsGkAtoms::x1
, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
,
26 {nsGkAtoms::y1
, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
,
28 {nsGkAtoms::x2
, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
,
30 {nsGkAtoms::y2
, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER
,
34 //----------------------------------------------------------------------
37 SVGLineElement::SVGLineElement(
38 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
39 : SVGLineElementBase(std::move(aNodeInfo
)) {}
41 void SVGLineElement::MaybeAdjustForZeroLength(float aX1
, float aY1
, float& aX2
,
43 if (aX1
== aX2
&& aY1
== aY2
) {
44 SVGContentUtils::AutoStrokeOptions strokeOptions
;
45 SVGContentUtils::GetStrokeOptions(&strokeOptions
, this, nullptr, nullptr,
46 SVGContentUtils::eIgnoreStrokeDashing
);
48 if (strokeOptions
.mLineCap
!= CapStyle::BUTT
) {
50 strokeOptions
.mLineWidth
/ SVG_ZERO_LENGTH_PATH_FIX_FACTOR
;
56 //----------------------------------------------------------------------
59 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGLineElement
)
61 //----------------------------------------------------------------------
63 already_AddRefed
<DOMSVGAnimatedLength
> SVGLineElement::X1() {
64 return mLengthAttributes
[ATTR_X1
].ToDOMAnimatedLength(this);
67 already_AddRefed
<DOMSVGAnimatedLength
> SVGLineElement::Y1() {
68 return mLengthAttributes
[ATTR_Y1
].ToDOMAnimatedLength(this);
71 already_AddRefed
<DOMSVGAnimatedLength
> SVGLineElement::X2() {
72 return mLengthAttributes
[ATTR_X2
].ToDOMAnimatedLength(this);
75 already_AddRefed
<DOMSVGAnimatedLength
> SVGLineElement::Y2() {
76 return mLengthAttributes
[ATTR_Y2
].ToDOMAnimatedLength(this);
79 //----------------------------------------------------------------------
82 SVGElement::LengthAttributesInfo
SVGLineElement::GetLengthInfo() {
83 return LengthAttributesInfo(mLengthAttributes
, sLengthInfo
,
84 std::size(sLengthInfo
));
87 //----------------------------------------------------------------------
88 // SVGGeometryElement methods
90 void SVGLineElement::GetMarkPoints(nsTArray
<SVGMark
>* aMarks
) {
93 GetAnimatedLengthValues(&x1
, &y1
, &x2
, &y2
, nullptr);
95 float angle
= std::atan2(y2
- y1
, x2
- x1
);
97 aMarks
->AppendElement(SVGMark(x1
, y1
, angle
, SVGMark::eStart
));
98 aMarks
->AppendElement(SVGMark(x2
, y2
, angle
, SVGMark::eEnd
));
101 void SVGLineElement::GetAsSimplePath(SimplePath
* aSimplePath
) {
102 float x1
, y1
, x2
, y2
;
103 GetAnimatedLengthValues(&x1
, &y1
, &x2
, &y2
, nullptr);
105 MaybeAdjustForZeroLength(x1
, y1
, x2
, y2
);
106 aSimplePath
->SetLine(x1
, y1
, x2
, y2
);
109 already_AddRefed
<Path
> SVGLineElement::BuildPath(PathBuilder
* aBuilder
) {
110 float x1
, y1
, x2
, y2
;
111 GetAnimatedLengthValues(&x1
, &y1
, &x2
, &y2
, nullptr);
113 MaybeAdjustForZeroLength(x1
, y1
, x2
, y2
);
114 aBuilder
->MoveTo(Point(x1
, y1
));
115 aBuilder
->LineTo(Point(x2
, y2
));
117 return aBuilder
->Finish();
120 bool SVGLineElement::GetGeometryBounds(Rect
* aBounds
,
121 const StrokeOptions
& aStrokeOptions
,
122 const Matrix
& aToBoundsSpace
,
123 const Matrix
* aToNonScalingStrokeSpace
) {
124 float x1
, y1
, x2
, y2
;
125 GetAnimatedLengthValues(&x1
, &y1
, &x2
, &y2
, nullptr);
127 if (aStrokeOptions
.mLineWidth
<= 0) {
128 *aBounds
= Rect(aToBoundsSpace
.TransformPoint(Point(x1
, y1
)), Size());
129 aBounds
->ExpandToEnclose(aToBoundsSpace
.TransformPoint(Point(x2
, y2
)));
133 // transform from non-scaling-stroke space to the space in which we compute
135 Matrix nonScalingToBounds
;
136 if (aToNonScalingStrokeSpace
) {
137 MOZ_ASSERT(!aToNonScalingStrokeSpace
->IsSingular());
138 Matrix nonScalingToUser
= aToNonScalingStrokeSpace
->Inverse();
139 nonScalingToBounds
= nonScalingToUser
* aToBoundsSpace
;
142 if (aStrokeOptions
.mLineCap
== CapStyle::ROUND
) {
143 if (!aToBoundsSpace
.IsRectilinear() ||
144 (aToNonScalingStrokeSpace
&&
145 !aToNonScalingStrokeSpace
->IsRectilinear())) {
146 // TODO: handle this case.
149 Rect
bounds(Point(x1
, y1
), Size());
150 bounds
.ExpandToEnclose(Point(x2
, y2
));
151 if (aToNonScalingStrokeSpace
) {
152 bounds
= aToNonScalingStrokeSpace
->TransformBounds(bounds
);
153 bounds
.Inflate(aStrokeOptions
.mLineWidth
/ 2.f
);
154 *aBounds
= nonScalingToBounds
.TransformBounds(bounds
);
156 bounds
.Inflate(aStrokeOptions
.mLineWidth
/ 2.f
);
157 *aBounds
= aToBoundsSpace
.TransformBounds(bounds
);
162 // Handle butt and square linecap, normal and non-scaling stroke cases
163 // together: start with endpoints (x1, y1), (x2, y2) in the stroke space,
164 // compute the four corners of the stroked line, transform the corners to
165 // bounds space, and compute bounds there.
167 if (aToNonScalingStrokeSpace
) {
168 Point nonScalingSpaceP1
, nonScalingSpaceP2
;
169 nonScalingSpaceP1
= aToNonScalingStrokeSpace
->TransformPoint(Point(x1
, y1
));
170 nonScalingSpaceP2
= aToNonScalingStrokeSpace
->TransformPoint(Point(x2
, y2
));
171 x1
= nonScalingSpaceP1
.x
;
172 y1
= nonScalingSpaceP1
.y
;
173 x2
= nonScalingSpaceP2
.x
;
174 y2
= nonScalingSpaceP2
.y
;
177 Float length
= Float(NS_hypot(x2
- x1
, y2
- y1
));
182 if (aStrokeOptions
.mLineCap
== CapStyle::BUTT
) {
184 xDelta
= yDelta
= 0.f
;
186 Float ratio
= aStrokeOptions
.mLineWidth
/ 2.f
/ length
;
187 xDelta
= ratio
* (y2
- y1
);
188 yDelta
= ratio
* (x2
- x1
);
190 points
[0] = Point(x1
- xDelta
, y1
+ yDelta
);
191 points
[1] = Point(x1
+ xDelta
, y1
- yDelta
);
192 points
[2] = Point(x2
+ xDelta
, y2
- yDelta
);
193 points
[3] = Point(x2
- xDelta
, y2
+ yDelta
);
195 MOZ_ASSERT(aStrokeOptions
.mLineCap
== CapStyle::SQUARE
);
197 xDelta
= yDelta
= aStrokeOptions
.mLineWidth
/ 2.f
;
198 points
[0] = Point(x1
- xDelta
, y1
+ yDelta
);
199 points
[1] = Point(x1
- xDelta
, y1
- yDelta
);
200 points
[2] = Point(x1
+ xDelta
, y1
- yDelta
);
201 points
[3] = Point(x1
+ xDelta
, y1
+ yDelta
);
203 Float ratio
= aStrokeOptions
.mLineWidth
/ 2.f
/ length
;
204 yDelta
= ratio
* (x2
- x1
);
205 xDelta
= ratio
* (y2
- y1
);
206 points
[0] = Point(x1
- yDelta
- xDelta
, y1
- xDelta
+ yDelta
);
207 points
[1] = Point(x1
- yDelta
+ xDelta
, y1
- xDelta
- yDelta
);
208 points
[2] = Point(x2
+ yDelta
+ xDelta
, y2
+ xDelta
- yDelta
);
209 points
[3] = Point(x2
+ yDelta
- xDelta
, y2
+ xDelta
+ yDelta
);
213 const Matrix
& toBoundsSpace
=
214 aToNonScalingStrokeSpace
? nonScalingToBounds
: aToBoundsSpace
;
216 *aBounds
= Rect(toBoundsSpace
.TransformPoint(points
[0]), Size());
217 for (uint32_t i
= 1; i
< 4; ++i
) {
218 aBounds
->ExpandToEnclose(toBoundsSpace
.TransformPoint(points
[i
]));
224 } // namespace mozilla::dom