add conic support to hairlines

git-svn-id: http://skia.googlecode.com/svn/trunk@9493 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/aaclip.cpp b/gm/aaclip.cpp
index f59f6aa..6ebb88c 100644
--- a/gm/aaclip.cpp
+++ b/gm/aaclip.cpp
@@ -9,6 +9,34 @@
 #include "SkCanvas.h"
 #include "SkPath.h"
 
+static void draw_conic(SkCanvas* canvas, SkScalar weight, const SkPaint& paint) {
+    SkPath path;
+    path.moveTo(100, 100);
+    path.conicTo(300, 100, 300, 300, weight);
+    canvas->drawPath(path, paint);
+}
+
+static void test_conic(SkCanvas* canvas) {
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setStyle(SkPaint::kStroke_Style);
+
+    static const struct {
+        SkScalar fWeight;
+        SkColor  fColor;
+    } gRec[] = {
+        { 2   , SK_ColorRED },
+        { 1   , SK_ColorGREEN },
+        { 0.5f, SK_ColorBLUE },
+    };
+    
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
+        paint.setColor(gRec[i].fColor);
+        draw_conic(canvas, gRec[i].fWeight, paint);
+        canvas->translate(-30, 30);
+    }
+}
+
 #include "SkGradientShader.h"
 static void test_shallow_gradient(SkCanvas* canvas, SkScalar width, SkScalar height) {
     SkColor colors[] = { 0xFF7F7F7F, 0xFF7F7F7F, 0xFF000000 };
@@ -219,6 +247,7 @@
     }
 
     virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        if (false) { test_conic(canvas); return; }
         if (false) {
             SkRect bounds;
             canvas->getClipBounds(&bounds);
diff --git a/include/core/SkGeometry.h b/include/core/SkGeometry.h
index 2c37fff..38cdf6e 100644
--- a/include/core/SkGeometry.h
+++ b/include/core/SkGeometry.h
@@ -251,4 +251,51 @@
     void computeFastBounds(SkRect* bounds) const;
 };
 
+#include "SkTemplates.h"
+
+/**
+ *  Help class to allocate storage for approximating a conic with N quads.
+ */
+class SkAutoConicToQuads {
+public:
+    SkAutoConicToQuads() : fQuadCount(0) {}
+
+    /**
+     *  Given a conic and a tolerance, return the array of points for the
+     *  approximating quad(s). Call countQuads() to know the number of quads
+     *  represented in these points.
+     *
+     *  The quads are allocated to share end-points. e.g. if there are 4 quads,
+     *  there will be 9 points allocated as follows
+     *      quad[0] == pts[0..2]
+     *      quad[1] == pts[2..4]
+     *      quad[2] == pts[4..6]
+     *      quad[3] == pts[6..8]
+     */
+    const SkPoint* computeQuads(const SkConic& conic, SkScalar tol) {
+        int pow2 = conic.computeQuadPOW2(tol);
+        fQuadCount = 1 << pow2;
+        SkPoint* pts = fStorage.reset(1 + 2 * fQuadCount);
+        conic.chopIntoQuadsPOW2(pts, pow2);
+        return pts;
+    }
+
+    const SkPoint* computeQuads(const SkPoint pts[3], SkScalar weight,
+                                SkScalar tol) {
+        SkConic conic;
+        conic.set(pts, weight);
+        return computeQuads(conic, tol);
+    }
+
+    int countQuads() const { return fQuadCount; }
+
+private:
+    enum {
+        kQuadCount = 8, // should handle most conics
+        kPointCount = 1 + 2 * kQuadCount,
+    };
+    SkAutoSTMalloc<kPointCount, SkPoint> fStorage;
+    int fQuadCount; // #quads for current usage
+};
+
 #endif
diff --git a/include/core/SkTemplates.h b/include/core/SkTemplates.h
index bbbed48..e8a8b61 100644
--- a/include/core/SkTemplates.h
+++ b/include/core/SkTemplates.h
@@ -366,7 +366,7 @@
     }
 
     // doesn't preserve contents
-    void reset(size_t count) {
+    T* reset(size_t count) {
         if (fPtr != fTStorage) {
             sk_free(fPtr);
         }
@@ -377,6 +377,7 @@
         } else {
             fPtr = NULL;
         }
+        return fPtr;
     }
 
     T* get() const { return fPtr; }
diff --git a/src/core/SkScan_Hairline.cpp b/src/core/SkScan_Hairline.cpp
index 3245355..f440d32 100644
--- a/src/core/SkScan_Hairline.cpp
+++ b/src/core/SkScan_Hairline.cpp
@@ -212,58 +212,55 @@
     }
 }
 
-static void hairquad(const SkPoint pts[3], const SkRegion* clip, SkBlitter* blitter, int level,
-                     void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion* clip, SkBlitter*))
-{
-#if 1
-    if (level > 0)
-    {
+typedef void (*LineProc)(const SkPoint&, const SkPoint&, const SkRegion*,
+                         SkBlitter*);
+
+static void hairquad(const SkPoint pts[3], const SkRegion* clip,
+                     SkBlitter* blitter, int level, LineProc lineproc) {
+    if (level > 0) {
         SkPoint tmp[5];
 
         SkChopQuadAtHalf(pts, tmp);
         hairquad(tmp, clip, blitter, level - 1, lineproc);
         hairquad(&tmp[2], clip, blitter, level - 1, lineproc);
-    }
-    else
+    } else {
         lineproc(pts[0], pts[2], clip, blitter);
-#else
-    int count = 1 << level;
-    const SkScalar dt = SkFixedToScalar(SK_Fixed1 >> level);
-    SkScalar t = dt;
-    SkPoint prevPt = pts[0];
-    for (int i = 1; i < count; i++) {
-        SkPoint nextPt;
-        SkEvalQuadAt(pts, t, &nextPt);
-        lineproc(prevPt, nextPt, clip, blitter);
-        t += dt;
-        prevPt = nextPt;
     }
-    // draw the last line explicitly to 1.0, in case t didn't match that exactly
-    lineproc(prevPt, pts[2], clip, blitter);
-#endif
 }
 
-static void haircubic(const SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter, int level,
-                      void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*))
-{
-    if (level > 0)
-    {
+static void haircubic(const SkPoint pts[4], const SkRegion* clip,
+                      SkBlitter* blitter, int level, LineProc lineproc) {
+    if (level > 0) {
         SkPoint tmp[7];
 
         SkChopCubicAt(pts, tmp, SK_Scalar1/2);
         haircubic(tmp, clip, blitter, level - 1, lineproc);
         haircubic(&tmp[3], clip, blitter, level - 1, lineproc);
-    }
-    else
+    } else {
         lineproc(pts[0], pts[3], clip, blitter);
+    }
 }
 
 #define kMaxCubicSubdivideLevel 6
 #define kMaxQuadSubdivideLevel  5
 
-static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter* blitter,
-                      void (*lineproc)(const SkPoint&, const SkPoint&, const SkRegion*, SkBlitter*))
-{
+static int compute_quad_level(const SkPoint pts[3]) {
+    int d = compute_int_quad_dist(pts);
+    /*  quadratics approach the line connecting their start and end points
+     4x closer with each subdivision, so we compute the number of
+     subdivisions to be the minimum need to get that distance to be less
+     than a pixel.
+     */
+    int level = (33 - SkCLZ(d)) >> 1;
+    // sanity check on level (from the previous version)
+    if (level > kMaxQuadSubdivideLevel) {
+        level = kMaxQuadSubdivideLevel;
+    }
+    return level;
+}
+
+static void hair_path(const SkPath& path, const SkRasterClip& rclip,
+                      SkBlitter* blitter, LineProc lineproc) {
     if (path.isEmpty()) {
         return;
     }
@@ -293,32 +290,36 @@
     SkPath::Iter    iter(path, false);
     SkPoint         pts[4];
     SkPath::Verb    verb;
+    SkAutoConicToQuads converter;
 
     while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
         switch (verb) {
+            case SkPath::kMove_Verb:
+                break;
             case SkPath::kLine_Verb:
                 lineproc(pts[0], pts[1], clip, blitter);
                 break;
-            case SkPath::kQuad_Verb: {
-                int d = compute_int_quad_dist(pts);
-                /*  quadratics approach the line connecting their start and end points
-                 4x closer with each subdivision, so we compute the number of
-                 subdivisions to be the minimum need to get that distance to be less
-                 than a pixel.
-                 */
-                int level = (33 - SkCLZ(d)) >> 1;
-    //          SkDebugf("----- distance %d computedLevel %d\n", d, computedLevel);
-                // sanity check on level (from the previous version)
-                if (level > kMaxQuadSubdivideLevel) {
-                    level = kMaxQuadSubdivideLevel;
+            case SkPath::kQuad_Verb:
+                hairquad(pts, clip, blitter, compute_quad_level(pts), lineproc);
+                break;
+            case SkPath::kConic_Verb: {
+                // how close should the quads be to the original conic?
+                const SkScalar tol = SK_Scalar1 / 4;
+                const SkPoint* quadPts = converter.computeQuads(pts,
+                                                       iter.conicWeight(), tol);
+                for (int i = 0; i < converter.countQuads(); ++i) {
+                    int level = compute_quad_level(quadPts);
+                    hairquad(quadPts, clip, blitter, level, lineproc);
+                    quadPts += 2;
                 }
-                hairquad(pts, clip, blitter, level, lineproc);
                 break;
             }
             case SkPath::kCubic_Verb:
                 haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc);
                 break;
-            default:
+            case SkPath::kClose_Verb:
+                break;
+            case SkPath::kDone_Verb:
                 break;
         }
     }