diff options
Diffstat (limited to 'dom/svg/SVGLineElement.cpp')
-rw-r--r-- | dom/svg/SVGLineElement.cpp | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/dom/svg/SVGLineElement.cpp b/dom/svg/SVGLineElement.cpp new file mode 100644 index 0000000000..7834bd14ad --- /dev/null +++ b/dom/svg/SVGLineElement.cpp @@ -0,0 +1,224 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/SVGLineElement.h" +#include "mozilla/dom/SVGLengthBinding.h" +#include "mozilla/dom/SVGLineElementBinding.h" +#include "mozilla/gfx/2D.h" + +NS_IMPL_NS_NEW_SVG_ELEMENT(Line) + +using namespace mozilla::gfx; + +namespace mozilla::dom { + +JSObject* SVGLineElement::WrapNode(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) { + return SVGLineElement_Binding::Wrap(aCx, this, aGivenProto); +} + +SVGElement::LengthInfo SVGLineElement::sLengthInfo[4] = { + {nsGkAtoms::x1, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, + SVGContentUtils::X}, + {nsGkAtoms::y1, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, + SVGContentUtils::Y}, + {nsGkAtoms::x2, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, + SVGContentUtils::X}, + {nsGkAtoms::y2, 0, SVGLength_Binding::SVG_LENGTHTYPE_NUMBER, + SVGContentUtils::Y}, +}; + +//---------------------------------------------------------------------- +// Implementation + +SVGLineElement::SVGLineElement( + already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) + : SVGLineElementBase(std::move(aNodeInfo)) {} + +void SVGLineElement::MaybeAdjustForZeroLength(float aX1, float aY1, float& aX2, + float aY2) { + if (aX1 == aX2 && aY1 == aY2) { + SVGContentUtils::AutoStrokeOptions strokeOptions; + SVGContentUtils::GetStrokeOptions(&strokeOptions, this, nullptr, nullptr, + SVGContentUtils::eIgnoreStrokeDashing); + + if (strokeOptions.mLineCap != CapStyle::BUTT) { + float tinyLength = + strokeOptions.mLineWidth / SVG_ZERO_LENGTH_PATH_FIX_FACTOR; + aX2 += tinyLength; + } + } +} + +//---------------------------------------------------------------------- +// nsINode methods + +NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGLineElement) + +//---------------------------------------------------------------------- + +already_AddRefed<DOMSVGAnimatedLength> SVGLineElement::X1() { + return mLengthAttributes[ATTR_X1].ToDOMAnimatedLength(this); +} + +already_AddRefed<DOMSVGAnimatedLength> SVGLineElement::Y1() { + return mLengthAttributes[ATTR_Y1].ToDOMAnimatedLength(this); +} + +already_AddRefed<DOMSVGAnimatedLength> SVGLineElement::X2() { + return mLengthAttributes[ATTR_X2].ToDOMAnimatedLength(this); +} + +already_AddRefed<DOMSVGAnimatedLength> SVGLineElement::Y2() { + return mLengthAttributes[ATTR_Y2].ToDOMAnimatedLength(this); +} + +//---------------------------------------------------------------------- +// SVGElement methods + +SVGElement::LengthAttributesInfo SVGLineElement::GetLengthInfo() { + return LengthAttributesInfo(mLengthAttributes, sLengthInfo, + ArrayLength(sLengthInfo)); +} + +//---------------------------------------------------------------------- +// SVGGeometryElement methods + +void SVGLineElement::GetMarkPoints(nsTArray<SVGMark>* aMarks) { + float x1, y1, x2, y2; + + GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr); + + float angle = std::atan2(y2 - y1, x2 - x1); + + aMarks->AppendElement(SVGMark(x1, y1, angle, SVGMark::eStart)); + aMarks->AppendElement(SVGMark(x2, y2, angle, SVGMark::eEnd)); +} + +void SVGLineElement::GetAsSimplePath(SimplePath* aSimplePath) { + float x1, y1, x2, y2; + GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr); + + MaybeAdjustForZeroLength(x1, y1, x2, y2); + aSimplePath->SetLine(x1, y1, x2, y2); +} + +already_AddRefed<Path> SVGLineElement::BuildPath(PathBuilder* aBuilder) { + float x1, y1, x2, y2; + GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr); + + MaybeAdjustForZeroLength(x1, y1, x2, y2); + aBuilder->MoveTo(Point(x1, y1)); + aBuilder->LineTo(Point(x2, y2)); + + return aBuilder->Finish(); +} + +bool SVGLineElement::GetGeometryBounds(Rect* aBounds, + const StrokeOptions& aStrokeOptions, + const Matrix& aToBoundsSpace, + const Matrix* aToNonScalingStrokeSpace) { + float x1, y1, x2, y2; + GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr); + + if (aStrokeOptions.mLineWidth <= 0) { + *aBounds = Rect(aToBoundsSpace.TransformPoint(Point(x1, y1)), Size()); + aBounds->ExpandToEnclose(aToBoundsSpace.TransformPoint(Point(x2, y2))); + return true; + } + + // transform from non-scaling-stroke space to the space in which we compute + // bounds + Matrix nonScalingToBounds; + if (aToNonScalingStrokeSpace) { + MOZ_ASSERT(!aToNonScalingStrokeSpace->IsSingular()); + Matrix nonScalingToUser = aToNonScalingStrokeSpace->Inverse(); + nonScalingToBounds = nonScalingToUser * aToBoundsSpace; + } + + if (aStrokeOptions.mLineCap == CapStyle::ROUND) { + if (!aToBoundsSpace.IsRectilinear() || + (aToNonScalingStrokeSpace && + !aToNonScalingStrokeSpace->IsRectilinear())) { + // TODO: handle this case. + return false; + } + Rect bounds(Point(x1, y1), Size()); + bounds.ExpandToEnclose(Point(x2, y2)); + if (aToNonScalingStrokeSpace) { + bounds = aToNonScalingStrokeSpace->TransformBounds(bounds); + bounds.Inflate(aStrokeOptions.mLineWidth / 2.f); + *aBounds = nonScalingToBounds.TransformBounds(bounds); + } else { + bounds.Inflate(aStrokeOptions.mLineWidth / 2.f); + *aBounds = aToBoundsSpace.TransformBounds(bounds); + } + return true; + } + + // Handle butt and square linecap, normal and non-scaling stroke cases + // together: start with endpoints (x1, y1), (x2, y2) in the stroke space, + // compute the four corners of the stroked line, transform the corners to + // bounds space, and compute bounds there. + + if (aToNonScalingStrokeSpace) { + Point nonScalingSpaceP1, nonScalingSpaceP2; + nonScalingSpaceP1 = aToNonScalingStrokeSpace->TransformPoint(Point(x1, y1)); + nonScalingSpaceP2 = aToNonScalingStrokeSpace->TransformPoint(Point(x2, y2)); + x1 = nonScalingSpaceP1.x; + y1 = nonScalingSpaceP1.y; + x2 = nonScalingSpaceP2.x; + y2 = nonScalingSpaceP2.y; + } + + Float length = Float(NS_hypot(x2 - x1, y2 - y1)); + Float xDelta; + Float yDelta; + Point points[4]; + + if (aStrokeOptions.mLineCap == CapStyle::BUTT) { + if (length == 0.f) { + xDelta = yDelta = 0.f; + } else { + Float ratio = aStrokeOptions.mLineWidth / 2.f / length; + xDelta = ratio * (y2 - y1); + yDelta = ratio * (x2 - x1); + } + points[0] = Point(x1 - xDelta, y1 + yDelta); + points[1] = Point(x1 + xDelta, y1 - yDelta); + points[2] = Point(x2 + xDelta, y2 - yDelta); + points[3] = Point(x2 - xDelta, y2 + yDelta); + } else { + MOZ_ASSERT(aStrokeOptions.mLineCap == CapStyle::SQUARE); + if (length == 0.f) { + xDelta = yDelta = aStrokeOptions.mLineWidth / 2.f; + points[0] = Point(x1 - xDelta, y1 + yDelta); + points[1] = Point(x1 - xDelta, y1 - yDelta); + points[2] = Point(x1 + xDelta, y1 - yDelta); + points[3] = Point(x1 + xDelta, y1 + yDelta); + } else { + Float ratio = aStrokeOptions.mLineWidth / 2.f / length; + yDelta = ratio * (x2 - x1); + xDelta = ratio * (y2 - y1); + points[0] = Point(x1 - yDelta - xDelta, y1 - xDelta + yDelta); + points[1] = Point(x1 - yDelta + xDelta, y1 - xDelta - yDelta); + points[2] = Point(x2 + yDelta + xDelta, y2 + xDelta - yDelta); + points[3] = Point(x2 + yDelta - xDelta, y2 + xDelta + yDelta); + } + } + + const Matrix& toBoundsSpace = + aToNonScalingStrokeSpace ? nonScalingToBounds : aToBoundsSpace; + + *aBounds = Rect(toBoundsSpace.TransformPoint(points[0]), Size()); + for (uint32_t i = 1; i < 4; ++i) { + aBounds->ExpandToEnclose(toBoundsSpace.TransformPoint(points[i])); + } + + return true; +} + +} // namespace mozilla::dom |