Bug 1090934 - Get bounds of line element using maths. r=jwatt
authorRobert Longson <longsonr@gmail.com>
Thu, 22 Jan 2015 09:36:08 +0000
changeset 252238 94c12d733e3b39a2e092f4948e7db3177eb11d7e
parent 252237 60b4850423d38796df17ed7490addeb525583cf6
child 252239 21fdc964a1828e63417fc936ad6316a9d55f6eaf
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwatt
bugs1090934
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1090934 - Get bounds of line element using maths. r=jwatt
dom/svg/SVGCircleElement.cpp
dom/svg/SVGCircleElement.h
dom/svg/SVGEllipseElement.cpp
dom/svg/SVGEllipseElement.h
dom/svg/SVGImageElement.cpp
dom/svg/SVGImageElement.h
dom/svg/SVGLineElement.cpp
dom/svg/SVGLineElement.h
dom/svg/SVGRectElement.cpp
dom/svg/SVGRectElement.h
dom/svg/nsSVGPathGeometryElement.h
dom/svg/nsSVGPolyElement.cpp
dom/svg/nsSVGPolyElement.h
layout/svg/nsSVGPathGeometryFrame.cpp
--- a/dom/svg/SVGCircleElement.cpp
+++ b/dom/svg/SVGCircleElement.cpp
@@ -78,17 +78,17 @@ SVGCircleElement::GetLengthInfo()
                               ArrayLength(sLengthInfo));
 }
 
 //----------------------------------------------------------------------
 // nsSVGPathGeometryElement methods
 
 bool
 SVGCircleElement::GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
-                                    const Matrix& aTransform)
+                                    CapStyle aCapStyle, const Matrix& aTransform)
 {
   float x, y, r;
   GetAnimatedLengthValues(&x, &y, &r, nullptr);
 
   if (r <= 0.f) {
     // Rendering of the element is disabled
     *aBounds = Rect(aTransform * Point(x, y), Size());
     return true;
--- a/dom/svg/SVGCircleElement.h
+++ b/dom/svg/SVGCircleElement.h
@@ -26,17 +26,17 @@ protected:
                                             already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
 
 public:
   // nsSVGSVGElement methods:
   virtual bool HasValidDimensions() const MOZ_OVERRIDE;
 
   // nsSVGPathGeometryElement methods:
   virtual bool GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
-                                 const Matrix& aTransform) MOZ_OVERRIDE;
+                                 CapStyle aCapStyle, const Matrix& aTransform) MOZ_OVERRIDE;
   virtual TemporaryRef<Path> BuildPath(PathBuilder* aBuilder) MOZ_OVERRIDE;
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
   // WebIDL
   already_AddRefed<SVGAnimatedLength> Cx();
   already_AddRefed<SVGAnimatedLength> Cy();
   already_AddRefed<SVGAnimatedLength> R();
--- a/dom/svg/SVGEllipseElement.cpp
+++ b/dom/svg/SVGEllipseElement.cpp
@@ -89,17 +89,17 @@ SVGEllipseElement::GetLengthInfo()
                               ArrayLength(sLengthInfo));
 }
 
 //----------------------------------------------------------------------
 // nsSVGPathGeometryElement methods
 
 bool
 SVGEllipseElement::GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
-                                     const Matrix& aTransform)
+                                     CapStyle aCapStyle, const Matrix& aTransform)
 {
   float x, y, rx, ry;
   GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr);
 
   if (rx <= 0.f || ry <= 0.f) {
     // Rendering of the element is disabled
     *aBounds = Rect(aTransform * Point(x, y), Size());
     return true;
--- a/dom/svg/SVGEllipseElement.h
+++ b/dom/svg/SVGEllipseElement.h
@@ -26,17 +26,17 @@ protected:
                                              already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
 
 public:
   // nsSVGSVGElement methods:
   virtual bool HasValidDimensions() const MOZ_OVERRIDE;
 
   // nsSVGPathGeometryElement methods:
   virtual bool GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
-                                 const Matrix& aTransform) MOZ_OVERRIDE;
+                                 CapStyle aCapStyle, const Matrix& aTransform) MOZ_OVERRIDE;
   virtual TemporaryRef<Path> BuildPath(PathBuilder* aBuilder) MOZ_OVERRIDE;
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
   // WebIDL
   already_AddRefed<SVGAnimatedLength> Cx();
   already_AddRefed<SVGAnimatedLength> Cy();
   already_AddRefed<SVGAnimatedLength> Rx();
--- a/dom/svg/SVGImageElement.cpp
+++ b/dom/svg/SVGImageElement.cpp
@@ -223,17 +223,17 @@ SVGImageElement::IsAttributeMapped(const
 
 //----------------------------------------------------------------------
 // nsSVGPathGeometryElement methods
 
 /* For the purposes of the update/invalidation logic pretend to
    be a rectangle. */
 bool
 SVGImageElement::GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
-                                   const Matrix& aTransform)
+                                   CapStyle aCapStyle, const Matrix& aTransform)
 {
   Rect rect;
   GetAnimatedLengthValues(&rect.x, &rect.y, &rect.width,
                           &rect.height, nullptr);
 
   if (rect.IsEmpty()) {
     // Rendering of the element disabled
     rect.SetEmpty(); // Make sure width/height are zero and not negative
--- a/dom/svg/SVGImageElement.h
+++ b/dom/svg/SVGImageElement.h
@@ -49,17 +49,17 @@ public:
   virtual void UnbindFromTree(bool aDeep, bool aNullParent) MOZ_OVERRIDE;
 
   virtual EventStates IntrinsicState() const MOZ_OVERRIDE;
 
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const MOZ_OVERRIDE;
 
   // nsSVGPathGeometryElement methods:
   virtual bool GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
-                                 const Matrix& aTransform) MOZ_OVERRIDE;
+                                 CapStyle cap, const Matrix& aTransform) MOZ_OVERRIDE;
   virtual TemporaryRef<Path> BuildPath(PathBuilder* aBuilder) MOZ_OVERRIDE;
 
   // nsSVGSVGElement methods:
   virtual bool HasValidDimensions() const MOZ_OVERRIDE;
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
   nsresult CopyInnerTo(mozilla::dom::Element* aDest);
--- a/dom/svg/SVGLineElement.cpp
+++ b/dom/svg/SVGLineElement.cpp
@@ -121,10 +121,71 @@ SVGLineElement::BuildPath(PathBuilder* a
   GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr);
 
   aBuilder->MoveTo(Point(x1, y1));
   aBuilder->LineTo(Point(x2, y2));
 
   return aBuilder->Finish();
 }
 
+bool
+SVGLineElement::GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
+                                  CapStyle aCapStyle, const Matrix& aTransform)
+{
+  float x1, y1, x2, y2;
+  GetAnimatedLengthValues(&x1, &y1, &x2, &y2, nullptr);
+
+  if (aStrokeWidth <= 0) {
+    *aBounds = Rect(aTransform * Point(x1, y1), Size());
+    aBounds->ExpandToEnclose(aTransform * Point(x2, y2));
+    return true;
+  }
+
+  if (aCapStyle == CapStyle::ROUND) {
+    if (!aTransform.IsRectilinear()) {
+      // TODO: handle this case.
+      return false;
+    }
+    Rect bounds(Point(x1, y1), Size());
+    bounds.ExpandToEnclose(Point(x2, y2));
+    bounds.Inflate(aStrokeWidth / 2.f);
+    *aBounds = aTransform.TransformBounds(bounds);
+    return true;
+  }
+
+  Float length = Float(NS_hypot(x2 - x1, y2 - y1));
+  Float xDelta;
+  Float yDelta;
+
+  if (aCapStyle == CapStyle::BUTT) {
+    if (length == 0.f) {
+      xDelta = yDelta = 0.f;
+    } else {
+      Float ratio = aStrokeWidth / 2.f / length;
+      xDelta = ratio * (y2 - y1);
+      yDelta = ratio * (x2 - x1);
+    }
+  } else {
+    MOZ_ASSERT(aCapStyle == CapStyle::SQUARE);
+    if (length == 0.f) {
+      xDelta = yDelta = aStrokeWidth / 2.f;
+    } else {
+      Float ratio = aStrokeWidth / 2.f / length;
+      xDelta = yDelta = ratio * (fabs(y2 - y1) + fabs(x2 - x1));
+    }
+  }
+
+  Point points[4];
+
+  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);
+
+  *aBounds = Rect(aTransform * points[0], Size());
+  for (uint32_t i = 1; i < 4; ++i) {
+    aBounds->ExpandToEnclose(aTransform * points[i]);
+  }
+  return true;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/svg/SVGLineElement.h
+++ b/dom/svg/SVGLineElement.h
@@ -29,16 +29,18 @@ public:
   // nsIContent interface
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* name) const MOZ_OVERRIDE;
 
   // nsSVGPathGeometryElement methods:
   virtual bool IsMarkable() MOZ_OVERRIDE { return true; }
   virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks) MOZ_OVERRIDE;
   virtual void GetAsSimplePath(SimplePath* aSimplePath) MOZ_OVERRIDE;
   virtual TemporaryRef<Path> BuildPath(PathBuilder* aBuilder) MOZ_OVERRIDE;
+  virtual bool GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
+                                 CapStyle cap, const Matrix& aTransform) MOZ_OVERRIDE;
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
   // WebIDL
   already_AddRefed<SVGAnimatedLength> X1();
   already_AddRefed<SVGAnimatedLength> Y1();
   already_AddRefed<SVGAnimatedLength> X2();
   already_AddRefed<SVGAnimatedLength> Y2();
--- a/dom/svg/SVGRectElement.cpp
+++ b/dom/svg/SVGRectElement.cpp
@@ -107,17 +107,17 @@ SVGRectElement::GetLengthInfo()
                               ArrayLength(sLengthInfo));
 }
 
 //----------------------------------------------------------------------
 // nsSVGPathGeometryElement methods
 
 bool
 SVGRectElement::GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
-                                  const Matrix& aTransform)
+                                  CapStyle aCapStyle, const Matrix& aTransform)
 {
   Rect rect;
   Float rx, ry;
   GetAnimatedLengthValues(&rect.x, &rect.y, &rect.width,
                           &rect.height, &rx, &ry, nullptr);
 
   if (rect.IsEmpty()) {
     // Rendering of the element disabled
--- a/dom/svg/SVGRectElement.h
+++ b/dom/svg/SVGRectElement.h
@@ -26,17 +26,17 @@ protected:
                                           already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
 
 public:
   // nsSVGSVGElement methods:
   virtual bool HasValidDimensions() const MOZ_OVERRIDE;
 
   // nsSVGPathGeometryElement methods:
   virtual bool GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
-                                 const Matrix& aTransform) MOZ_OVERRIDE;
+                                 CapStyle aCapStyle, const Matrix& aTransform) MOZ_OVERRIDE;
   virtual void GetAsSimplePath(SimplePath* aSimplePath) MOZ_OVERRIDE;
   virtual TemporaryRef<Path> BuildPath(PathBuilder* aBuilder = nullptr) MOZ_OVERRIDE;
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
   // WebIDL
   already_AddRefed<SVGAnimatedLength> X();
   already_AddRefed<SVGAnimatedLength> Y();
--- a/dom/svg/nsSVGPathGeometryElement.h
+++ b/dom/svg/nsSVGPathGeometryElement.h
@@ -26,16 +26,17 @@ struct nsSVGMark {
     x(aX), y(aY), angle(aAngle), type(aType) {}
 };
 
 typedef mozilla::dom::SVGGraphicsElement nsSVGPathGeometryElementBase;
 
 class nsSVGPathGeometryElement : public nsSVGPathGeometryElementBase
 {
 protected:
+  typedef mozilla::gfx::CapStyle CapStyle;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::FillRule FillRule;
   typedef mozilla::gfx::Float Float;
   typedef mozilla::gfx::Matrix Matrix;
   typedef mozilla::gfx::Path Path;
   typedef mozilla::gfx::Point Point;
   typedef mozilla::gfx::PathBuilder PathBuilder;
   typedef mozilla::gfx::Rect Rect;
@@ -72,17 +73,17 @@ public:
 
   /**
    * A method that can be faster than using a Moz2D Path and calling GetBounds/
    * GetStrokedBounds on it.  It also helps us avoid rounding error for simple
    * shapes and simple transforms where the Moz2D Path backends can fail to
    * produce the clean integer bounds that content authors expect in some cases.
    */
   virtual bool GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
-                                 const Matrix& aTransform) {
+                                 CapStyle aCapStyle, const Matrix& aTransform) {
     return false;
   }
 
   /**
    * For use with GetAsSimplePath.
    */
   class SimplePath
   {
--- a/dom/svg/nsSVGPolyElement.cpp
+++ b/dom/svg/nsSVGPolyElement.cpp
@@ -117,17 +117,17 @@ nsSVGPolyElement::GetMarkPoints(nsTArray
   }
 
   aMarks->LastElement().angle = prevAngle;
   aMarks->LastElement().type = nsSVGMark::eEnd;
 }
 
 bool
 nsSVGPolyElement::GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
-                                    const Matrix& aTransform)
+                                    CapStyle aCapStyle, const Matrix& aTransform)
 {
   const SVGPointList &points = mPoints.GetAnimValue();
 
   if (!points.Length()) {
     // Rendering of the element is disabled
     aBounds->SetEmpty();
     return true;
   }
--- a/dom/svg/nsSVGPolyElement.h
+++ b/dom/svg/nsSVGPolyElement.h
@@ -41,17 +41,17 @@ public:
   // nsSVGElement methods:
   virtual bool HasValidDimensions() const MOZ_OVERRIDE;
 
   // nsSVGPathGeometryElement methods:
   virtual bool AttributeDefinesGeometry(const nsIAtom *aName) MOZ_OVERRIDE;
   virtual bool IsMarkable() MOZ_OVERRIDE { return true; }
   virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks) MOZ_OVERRIDE;
   virtual bool GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
-                                 const Matrix& aTransform) MOZ_OVERRIDE;
+                                 CapStyle aCapStyle, const Matrix& aTransform) MOZ_OVERRIDE;
 
   // WebIDL
   already_AddRefed<mozilla::DOMSVGPointList> Points();
   already_AddRefed<mozilla::DOMSVGPointList> AnimatedPoints();
 
 protected:
   SVGAnimatedPointList mPoints;
 };
--- a/layout/svg/nsSVGPathGeometryFrame.cpp
+++ b/layout/svg/nsSVGPathGeometryFrame.cpp
@@ -466,19 +466,27 @@ nsSVGPathGeometryFrame::GetBBoxContribut
                   StyleSVG()->mFill.mType != eStyleSVGPaintType_None);
 
   bool getStroke = (aFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) ||
                    ((aFlags & nsSVGUtils::eBBoxIncludeStroke) &&
                     nsSVGUtils::HasStroke(this));
 
   bool gotSimpleBounds = false;
   if (!StyleSVGReset()->HasNonScalingStroke()) {
-    Float strokeWidth = getStroke ? nsSVGUtils::GetStrokeWidth(this) : 0.f;
+    SVGContentUtils::AutoStrokeOptions strokeOptions;
+    strokeOptions.mLineWidth = 0.f;
+    if (getStroke) {
+      SVGContentUtils::GetStrokeOptions(&strokeOptions, element,
+        StyleContext(), nullptr,
+        SVGContentUtils::eIgnoreStrokeDashing);
+    }
     Rect simpleBounds;
-    gotSimpleBounds = element->GetGeometryBounds(&simpleBounds, strokeWidth,
+    gotSimpleBounds = element->GetGeometryBounds(&simpleBounds,
+                                                 strokeOptions.mLineWidth,
+                                                 strokeOptions.mLineCap,
                                                  aToBBoxUserspace);
     if (gotSimpleBounds) {
       bbox = simpleBounds;
     }
   }
 
   if (!gotSimpleBounds) {
     // Get the bounds using a Moz2D Path object (more expensive):