Bug 1090211 - Calculate the bounds of circles and ellipses using Math when we have a rectilinear transform. r=longsonr
authorJonathan Watt <jwatt@jwatt.org>
Wed, 29 Oct 2014 01:59:36 +0000
changeset 212801 b51051e5ac2e56f0bbd27070f48fbd0fad4e5875
parent 212800 138f767ee2243f85845e7820f7d51bccb399805f
child 212802 1240f21309cd5e7b807b86a83f917a6e5e0e6c93
push id27730
push usercbook@mozilla.com
push dateWed, 29 Oct 2014 12:26:03 +0000
treeherdermozilla-central@fe5c1cb8075a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslongsonr
bugs1090211
milestone36.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 1090211 - Calculate the bounds of circles and ellipses using Math when we have a rectilinear transform. r=longsonr
dom/svg/SVGCircleElement.cpp
dom/svg/SVGCircleElement.h
dom/svg/SVGEllipseElement.cpp
dom/svg/SVGEllipseElement.h
dom/svg/nsSVGPathGeometryElement.h
--- a/dom/svg/SVGCircleElement.cpp
+++ b/dom/svg/SVGCircleElement.cpp
@@ -76,16 +76,44 @@ SVGCircleElement::GetLengthInfo()
 {
   return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
                               ArrayLength(sLengthInfo));
 }
 
 //----------------------------------------------------------------------
 // nsSVGPathGeometryElement methods
 
+bool
+SVGCircleElement::GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
+                                    const Matrix& aTransform)
+{
+  float x, y, r;
+  GetAnimatedLengthValues(&x, &y, &r, nullptr);
+
+  if (r <= 0.f) {
+    // Rendering of the element is disabled
+    aBounds->MoveTo(x, y);
+    aBounds->SetEmpty();
+    return true;
+  }
+
+  if (aTransform.IsRectilinear()) {
+    // Optimize the case where we can treat the circle as a rectangle and
+    // still get tight bounds.
+    if (aStrokeWidth > 0.f) {
+      r += aStrokeWidth / 2.f;
+    }
+    Rect rect(x - r, y - r, 2 * r, 2 * r);
+    *aBounds = aTransform.TransformBounds(rect);
+    return true;
+  }
+
+  return false;
+}
+
 TemporaryRef<Path>
 SVGCircleElement::BuildPath(PathBuilder* aBuilder)
 {
   float x, y, r;
   GetAnimatedLengthValues(&x, &y, &r, nullptr);
 
   if (r <= 0.0f) {
     return nullptr;
--- a/dom/svg/SVGCircleElement.h
+++ b/dom/svg/SVGCircleElement.h
@@ -25,16 +25,18 @@ protected:
   friend nsresult (::NS_NewSVGCircleElement(nsIContent **aResult,
                                             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;
   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
@@ -87,16 +87,45 @@ SVGEllipseElement::GetLengthInfo()
 {
   return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
                               ArrayLength(sLengthInfo));
 }
 
 //----------------------------------------------------------------------
 // nsSVGPathGeometryElement methods
 
+bool
+SVGEllipseElement::GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
+                                     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->MoveTo(x, y);
+    aBounds->SetEmpty();
+    return true;
+  }
+
+  if (aTransform.IsRectilinear()) {
+    // Optimize the case where we can treat the ellipse as a rectangle and
+    // still get tight bounds.
+    if (aStrokeWidth > 0.f) {
+      rx += aStrokeWidth / 2.f;
+      ry += aStrokeWidth / 2.f;
+    }
+    Rect rect(x - rx, y - ry, 2 * rx, 2 * ry);
+    *aBounds = aTransform.TransformBounds(rect);
+    return true;
+  }
+
+  return false;
+}
+
 TemporaryRef<Path>
 SVGEllipseElement::BuildPath(PathBuilder* aBuilder)
 {
   float x, y, rx, ry;
   GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr);
 
   if (rx <= 0.0f || ry <= 0.0f) {
     return nullptr;
--- a/dom/svg/SVGEllipseElement.h
+++ b/dom/svg/SVGEllipseElement.h
@@ -25,16 +25,18 @@ protected:
   friend nsresult (::NS_NewSVGEllipseElement(nsIContent **aResult,
                                              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;
   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/nsSVGPathGeometryElement.h
+++ b/dom/svg/nsSVGPathGeometryElement.h
@@ -65,16 +65,22 @@ public:
    * This could be moved up to a more general class so it can be used for non-leaf
    * elements, but that would require care and for now there's no need.
    */
   bool GeometryDependsOnCoordCtx();
 
   virtual bool IsMarkable();
   virtual void GetMarkPoints(nsTArray<nsSVGMark> *aMarks);
 
+  /**
+   * 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) {
     return false;
   }
 
   /**
    * For use with GetAsSimplePath.
    */