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