tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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