Improve gpu path subdiv with perspective, remove tolerance scale, fix comment

Review URL: http://codereview.appspot.com/4993041/



git-svn-id: http://skia.googlecode.com/svn/trunk@2239 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index 639e25d..b857dda 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -692,11 +692,6 @@
         GR_STATIC_ASSERT(4 == OFFSCREEN_SSAA_SCALE);
         desc.fAALevel = kNone_GrAALevel;
     }
-    // Avoid overtesselating paths in AA buffers; may unduly reduce quality
-    // of simple circles?
-    if (pr) {
-        //pr->scaleCurveTolerance(GrIntToScalar(record->fScale));
-    }
     
     desc.fWidth *= record->fScale;
     desc.fHeight *= record->fScale;
@@ -881,10 +876,6 @@
 void GrContext::cleanupOffscreenAA(GrDrawTarget* target,
                                    GrPathRenderer* pr,
                                    OffscreenRecord* record) {
-    if (pr) {
-        // Counterpart of scale() in prepareForOffscreenAA()
-        //pr->scaleCurveTolerance(SkScalarInvert(SkIntToScalar(record->fScale)));
-    }
     target->restoreDrawState(record->fSavedState);
 }
 
diff --git a/gpu/src/GrDefaultPathRenderer.cpp b/gpu/src/GrDefaultPathRenderer.cpp
index b70b863..3323c91 100644
--- a/gpu/src/GrDefaultPathRenderer.cpp
+++ b/gpu/src/GrDefaultPathRenderer.cpp
@@ -374,18 +374,9 @@
                                        bool stencilOnly) {
 
     GrMatrix viewM = fTarget->getViewMatrix();
-    // In order to tesselate the path we get a bound on how much the matrix can
-    // stretch when mapping to screen coordinates.
-    GrScalar stretch = viewM.getMaxStretch();
-    bool useStretch = stretch > 0;
-    GrScalar tol = fCurveTolerance;
+    GrScalar tol = GR_Scalar1;
+    tol = GrPathUtils::scaleToleranceToSrc(tol, viewM);
 
-    if (!useStretch) {
-        // TODO: deal with perspective in some better way.
-        tol /= 10;
-    } else {
-        tol = GrScalarDiv(tol, stretch);
-    }
     // FIXME: It's really dumb that we recreate the verts for a new vertex
     // layout. We only do that because the GrDrawTarget API doesn't allow
     // us to change the vertex layout after reserveVertexSpace(). We won't
diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp
index 06a00e4..929941a 100644
--- a/gpu/src/GrPathRenderer.cpp
+++ b/gpu/src/GrPathRenderer.cpp
@@ -9,8 +9,7 @@
 #include "GrPathRenderer.h"
 
 GrPathRenderer::GrPathRenderer()
-    : fCurveTolerance (GR_Scalar1)
-    , fPath(NULL)
+    : fPath(NULL)
     , fTarget(NULL) {
 }
 
diff --git a/gpu/src/GrPathRenderer.h b/gpu/src/GrPathRenderer.h
index 4694de5..815da47 100644
--- a/gpu/src/GrPathRenderer.h
+++ b/gpu/src/GrPathRenderer.h
@@ -36,8 +36,8 @@
      * This is called to install custom path renderers in every GrContext at
      * create time. The default implementation in GrCreatePathRenderer_none.cpp
      * does not add any additional renderers. Link against another
-     * implementation to install your own. The most recently added is the
-     * most preferred path renderer.
+     * implementation to install your own. The first added is the most preferred
+     * path renderer, second is second most preferred, etc.
      *
      * @param context   the context that will use the path renderer
      * @param flags     flags indicating how path renderers will be used
@@ -170,16 +170,6 @@
     }
 
     /**
-     * Multiply curve tolerance by the given value, increasing or decreasing
-     * the maximum error permitted in tesselating curves with short straight
-     * line segments.
-     */
-    void scaleCurveTolerance(GrScalar multiplier) {
-        GrAssert(multiplier > 0);
-        fCurveTolerance = SkScalarMul(fCurveTolerance, multiplier);
-    }
-
-    /**
      * Helper that sets a path and automatically remove it in destructor.
      */
     class AutoClearPath {
@@ -224,7 +214,6 @@
     virtual void pathWasSet() {}
     virtual void pathWillClear() {}
 
-    GrScalar fCurveTolerance;
     const SkPath*               fPath;
     GrDrawTarget*               fTarget;
     GrPathFill                  fFill;
diff --git a/gpu/src/GrPathUtils.cpp b/gpu/src/GrPathUtils.cpp
index 429b294..b7dc4b6 100644
--- a/gpu/src/GrPathUtils.cpp
+++ b/gpu/src/GrPathUtils.cpp
@@ -8,10 +8,27 @@
 
 
 #include "GrPathUtils.h"
+
 #include "GrPoint.h"
 
+GrScalar GrPathUtils::scaleToleranceToSrc(GrScalar devTol,
+                                          const GrMatrix& viewM) {
+    // In order to tesselate the path we get a bound on how much the matrix can
+    // stretch when mapping to screen coordinates.
+    GrScalar stretch = viewM.getMaxStretch();
+    GrScalar srcTol = devTol;
+
+    if (stretch < 0) {
+        // TODO: deal with perspective in some better way.
+        srcTol /= 5;
+        stretch = -stretch;
+    }
+    srcTol = GrScalarDiv(srcTol, stretch);
+    return srcTol;
+}
+
 static const int MAX_POINTS_PER_CURVE = 1 << 10;
-const GrScalar GrPathUtils::gMinCurveTol (GrFloatToScalar(0.0001f));
+static const GrScalar gMinCurveTol = GrFloatToScalar(0.0001f);
 
 uint32_t GrPathUtils::quadraticPointCount(const GrPoint points[],
                                           GrScalar tol) {
diff --git a/gpu/src/GrPathUtils.h b/gpu/src/GrPathUtils.h
index cde5601..8d77982 100644
--- a/gpu/src/GrPathUtils.h
+++ b/gpu/src/GrPathUtils.h
@@ -10,41 +10,42 @@
 #ifndef GrPathUtils_DEFINED
 #define GrPathUtils_DEFINED
 
-#include "GrNoncopyable.h"
-#include "GrPoint.h"
+#include "GrMatrix.h"
 #include "GrPath.h"
 
+class GrPoint;
+
 /**
  *  Utilities for evaluating paths.
  */
-class GrPathUtils : public GrNoncopyable {
-public:
-    /// Since we divide by tol if we're computing exact worst-case bounds,
-    /// very small tolerances will be increased to gMinCurveTol.
-    static int worstCasePointCount(const GrPath&,
-                                   int* subpaths,
-                                   GrScalar tol);
-    /// Since we divide by tol if we're computing exact worst-case bounds,
-    /// very small tolerances will be increased to gMinCurveTol.
-    static uint32_t quadraticPointCount(const GrPoint points[], GrScalar tol);
-    static uint32_t generateQuadraticPoints(const GrPoint& p0,
-                                            const GrPoint& p1,
-                                            const GrPoint& p2,
-                                            GrScalar tolSqd,
-                                            GrPoint** points,
-                                            uint32_t pointsLeft);
-    /// Since we divide by tol if we're computing exact worst-case bounds,
-    /// very small tolerances will be increased to gMinCurveTol.
-    static uint32_t cubicPointCount(const GrPoint points[], GrScalar tol);
-    static uint32_t generateCubicPoints(const GrPoint& p0,
-                                        const GrPoint& p1,
-                                        const GrPoint& p2,
-                                        const GrPoint& p3,
-                                        GrScalar tolSqd,
-                                        GrPoint** points,
-                                        uint32_t pointsLeft);
+namespace GrPathUtils {
+    GrScalar scaleToleranceToSrc(GrScalar devTol,
+                                 const GrMatrix& viewM);
 
-private:
-    static const GrScalar gMinCurveTol;
+    /// Since we divide by tol if we're computing exact worst-case bounds,
+    /// very small tolerances will be increased to gMinCurveTol.
+    int worstCasePointCount(const GrPath&,
+                            int* subpaths,
+                            GrScalar tol);
+    /// Since we divide by tol if we're computing exact worst-case bounds,
+    /// very small tolerances will be increased to gMinCurveTol.
+    uint32_t quadraticPointCount(const GrPoint points[], GrScalar tol);
+    uint32_t generateQuadraticPoints(const GrPoint& p0,
+                                     const GrPoint& p1,
+                                     const GrPoint& p2,
+                                     GrScalar tolSqd,
+                                     GrPoint** points,
+                                     uint32_t pointsLeft);
+    /// Since we divide by tol if we're computing exact worst-case bounds,
+    /// very small tolerances will be increased to gMinCurveTol.
+    uint32_t cubicPointCount(const GrPoint points[], GrScalar tol);
+    uint32_t generateCubicPoints(const GrPoint& p0,
+                                 const GrPoint& p1,
+                                 const GrPoint& p2,
+                                 const GrPoint& p3,
+                                 GrScalar tolSqd,
+                                 GrPoint** points,
+                                 uint32_t pointsLeft);
+
 };
 #endif
diff --git a/gpu/src/GrTesselatedPathRenderer.cpp b/gpu/src/GrTesselatedPathRenderer.cpp
index 15e3cc0..dd281ed 100644
--- a/gpu/src/GrTesselatedPathRenderer.cpp
+++ b/gpu/src/GrTesselatedPathRenderer.cpp
@@ -351,18 +351,9 @@
     GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace());
 
     GrMatrix viewM = fTarget->getViewMatrix();
-    // In order to tesselate the path we get a bound on how much the matrix can
-    // stretch when mapping to screen coordinates.
-    GrScalar stretch = viewM.getMaxStretch();
-    bool useStretch = stretch > 0;
-    GrScalar tol = fCurveTolerance;
 
-    if (!useStretch) {
-        // TODO: deal with perspective in some better way.
-        tol /= 10;
-    } else {
-        tol = GrScalarDiv(tol, stretch);
-    }
+    GrScalar tol = GR_Scalar1;
+    tol = GrPathUtils::scaleToleranceToSrc(tol, viewM);
     GrScalar tolSqd = GrMul(tol, tol);
 
     int subpathCnt;
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index 7c2109c..333749c 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -501,10 +501,12 @@
     void toDumpString(SkString*) const;
 
     /**
-     * Calculates the maximum stretching factor of the matrix. Only defined if
-     * the matrix does not have perspective.
+     * Calculates the maximum stretching factor of the matrix. If the matrix has
+     * perspective the max stretch at the origin (in the pre-matrix space) is
+     * computed and returned as a negative.
      *
-     * @return maximum strecthing factor or negative if matrix has perspective.
+     * @return maximum strecthing factor or negative max stretching factor at
+     * the origin if matrix has perspective.
      */
     SkScalar getMaxStretch() const;
 
diff --git a/samplecode/SampleHairCurves.cpp b/samplecode/SampleHairCurves.cpp
index 152cbeb..a3ea5e1 100644
--- a/samplecode/SampleHairCurves.cpp
+++ b/samplecode/SampleHairCurves.cpp
@@ -28,6 +28,11 @@
 
 
     virtual void onDrawContent(SkCanvas* canvas) {
+        SkMatrix m;
+        m.reset();
+        m.setPerspX(0.00020);
+        canvas->concat(m);
+
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setStyle(SkPaint::kStroke_Style);
@@ -38,6 +43,7 @@
         SkPath curves;
         SkPath hulls;
         SkPath ctrlPts;
+
         for (int i = 0; i < 100; ++i) {
             SkScalar pts[] = {
                 rand.nextUScalar1(), rand.nextUScalar1(),
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index a525f11..6fc6e5d 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -1674,10 +1674,6 @@
 SkScalar SkMatrix::getMaxStretch() const {
     TypeMask mask = this->getType();
 
-    if (mask & kPerspective_Mask) {
-        return -SK_Scalar1;
-    }
-    
     SkScalar stretch;
     
     if (this->isIdentity()) {
@@ -1702,7 +1698,8 @@
         // and roots are guaraunteed to be pos and real).
         SkScalar largerRoot;
         SkScalar bSqd = SkScalarMul(b,b);
-        if (bSqd <= SkFloatToScalar(1e-10)) { // will be true if upper left 2x2 is orthogonal, which is common, so save some math
+        // if upper left 2x2 is orthogonal save some math
+        if (bSqd <= SK_ScalarNearlyZero) {
             largerRoot = SkMaxScalar(a, c);
         } else {
             SkScalar aminusc = a - c;
@@ -1710,8 +1707,17 @@
             SkScalar x = SkScalarSqrt(SkScalarMul(aminusc, aminusc) + 4 * bSqd) / 2;
             largerRoot = apluscdiv2 + x;
         }
-        
         stretch = SkScalarSqrt(largerRoot);
+        if (mask & kPerspective_Mask) {
+            stretch = -stretch;
+            if (fMat[kMPersp2] != kMatrix22Elem) {
+#if defined(SK_SCALAR_IS_FLOAT)
+                stretch /= fMat[kMPersp2];
+#else
+                stretch = SkFractDiv(stretch, fMat[kMPersp2]);
+#endif
+            }
+        }
     }
 #if defined(SK_DEBUG) && 0
     // test a bunch of vectors. None should be scaled by more than stretch