Backed out 4 changesets (bug 1825722) for causing reftest failures CLOSED TREE
[gecko.git] / dom / svg / SVGLineElement.cpp
blob7834bd14ad055f82ede20959fd4fb3ed93fd4ea9
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,
25 SVGContentUtils::X},
26 {nsGkAtoms::y1, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
27 SVGContentUtils::Y},
28 {nsGkAtoms::x2, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
29 SVGContentUtils::X},
30 {nsGkAtoms::y2, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER,
31 SVGContentUtils::Y},
34 //----------------------------------------------------------------------
35 // Implementation
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,
42 float aY2) {
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) {
49 float tinyLength =
50 strokeOptions.mLineWidth / SVG_ZERO_LENGTH_PATH_FIX_FACTOR;
51 aX2 += tinyLength;
56 //----------------------------------------------------------------------
57 // nsINode methods
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 //----------------------------------------------------------------------
80 // SVGElement methods
82 SVGElement::LengthAttributesInfo SVGLineElement::GetLengthInfo() {
83 return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
84 ArrayLength(sLengthInfo));
87 //----------------------------------------------------------------------
88 // SVGGeometryElement methods
90 void SVGLineElement::GetMarkPoints(nsTArray<SVGMark>* aMarks) {
91 float x1, y1, x2, y2;
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)));
130 return true;
133 // transform from non-scaling-stroke space to the space in which we compute
134 // bounds
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.
147 return false;
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);
155 } else {
156 bounds.Inflate(aStrokeOptions.mLineWidth / 2.f);
157 *aBounds = aToBoundsSpace.TransformBounds(bounds);
159 return true;
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));
178 Float xDelta;
179 Float yDelta;
180 Point points[4];
182 if (aStrokeOptions.mLineCap == CapStyle::BUTT) {
183 if (length == 0.f) {
184 xDelta = yDelta = 0.f;
185 } else {
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);
194 } else {
195 MOZ_ASSERT(aStrokeOptions.mLineCap == CapStyle::SQUARE);
196 if (length == 0.f) {
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);
202 } else {
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]));
221 return true;
224 } // namespace mozilla::dom