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