SVGLineElement.cpp 7.9 KB

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