Add convex path renderer (disabled)
authorbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 17 Jan 2012 14:25:10 +0000 (14:25 +0000)
committerbsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 17 Jan 2012 14:25:10 +0000 (14:25 +0000)
Review URL: http://codereview.appspot.com/5533061/

git-svn-id: http://skia.googlecode.com/svn/trunk@3040 2bbb7eff-a529-9590-31e7-b0007b416f81

12 files changed:
gm/convexpaths.cpp [new file with mode: 0644]
gyp/gmslides.gypi
gyp/gpu.gyp
src/gpu/GrAAConvexPathRenderer.cpp [new file with mode: 0644]
src/gpu/GrAAConvexPathRenderer.h [new file with mode: 0644]
src/gpu/GrAAHairLinePathRenderer.cpp
src/gpu/GrAddPathRenderers_default.cpp [moved from src/gpu/GrAddPathRenderers_aahairline.cpp with 63% similarity]
src/gpu/GrDrawState.h
src/gpu/GrGLProgram.cpp
src/gpu/GrGpuGLShaders.cpp
src/gpu/GrPathUtils.cpp
src/gpu/GrPathUtils.h

diff --git a/gm/convexpaths.cpp b/gm/convexpaths.cpp
new file mode 100644 (file)
index 0000000..a7e3119
--- /dev/null
@@ -0,0 +1,136 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "gm.h"
+#include "SkRandom.h"
+#include "SkTArray.h"
+
+namespace skiagm {
+
+class ConvexPathsGM : public GM {
+public:
+    ConvexPathsGM() {
+        this->setBGColor(0xFFDDDDDD);
+        this->makePaths();
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("convexpaths");
+    }
+
+
+    virtual SkISize onISize() {
+        return make_isize(1200, 900);
+    }
+
+    void makePaths() {
+        fPaths.push_back().addRect(0, 0,
+                                   100 * SK_Scalar1, 100 * SK_Scalar1,
+                                   SkPath::kCW_Direction);
+
+        fPaths.push_back().addRect(0, 0,
+                                   100 * SK_Scalar1, 100 * SK_Scalar1,
+                                   SkPath::kCCW_Direction);
+
+        fPaths.push_back().addCircle(50  * SK_Scalar1, 50  * SK_Scalar1,
+                                     50  * SK_Scalar1, SkPath::kCW_Direction);
+
+        fPaths.push_back().addCircle(50  * SK_Scalar1, 50  * SK_Scalar1,
+                                     40  * SK_Scalar1, SkPath::kCCW_Direction);
+
+        fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0,
+                                                    50 * SK_Scalar1,
+                                                    100 * SK_Scalar1),
+                                   SkPath::kCW_Direction);
+
+        fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0,
+                                                    100 * SK_Scalar1,
+                                                    50 * SK_Scalar1),
+                                   SkPath::kCCW_Direction);
+
+        fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0,
+                                                    100 * SK_Scalar1,
+                                                    5 * SK_Scalar1),
+                                   SkPath::kCCW_Direction);
+
+        fPaths.push_back().addOval(SkRect::MakeXYWH(0, 0,
+                                                    SK_Scalar1,
+                                                    100 * SK_Scalar1),
+                                   SkPath::kCCW_Direction);
+
+        fPaths.push_back().addRect(0, 0,
+                                   100 * SK_Scalar1, 100 * SK_Scalar1,
+                                   SkPath::kCCW_Direction);
+
+        fPaths.push_back().addRoundRect(SkRect::MakeXYWH(0, 0,
+                                                         SK_Scalar1 * 100,
+                                                         SK_Scalar1 * 100),
+                                        40 * SK_Scalar1, 20 * SK_Scalar1,
+                                        SkPath::kCW_Direction);
+
+        fPaths.push_back().addRoundRect(SkRect::MakeXYWH(0, 0,
+                                                         SK_Scalar1 * 100,
+                                                         SK_Scalar1 * 100),
+                                        20 * SK_Scalar1, 40 * SK_Scalar1,
+                                        SkPath::kCCW_Direction);
+        /*
+        fPaths.push_back().arcTo(SkRect::MakeXYWH(0, 0,
+                                                  50 * SK_Scalar1,
+                                                  100 * SK_Scalar1),
+                                 25 * SK_Scalar1,  130 * SK_Scalar1, false);
+        */
+
+        // point degenerate
+        fPaths.push_back().lineTo(0,0);
+        fPaths.push_back().quadTo(0,0,0,0);
+        fPaths.push_back().cubicTo(0,0,0,0,0,0);
+
+        // line degenerate
+        fPaths.push_back().lineTo(100 * SK_Scalar1, 100 * SK_Scalar1);
+        fPaths.push_back().quadTo(100 * SK_Scalar1, 100 * SK_Scalar1, 0, 0);
+        fPaths.push_back().quadTo(100 * SK_Scalar1, 100 * SK_Scalar1,
+                                  50 * SK_Scalar1, 50 * SK_Scalar1);
+        fPaths.push_back().quadTo(50 * SK_Scalar1, 50 * SK_Scalar1,
+                                  100 * SK_Scalar1, 100 * SK_Scalar1);
+        fPaths.push_back().cubicTo(0, 0,
+                                   0, 0,
+                                   100 * SK_Scalar1, 100 * SK_Scalar1);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    SkRandom rand;
+    canvas->translate(20 * SK_Scalar1, 20 * SK_Scalar1);
+    for (int i = 0 ; i < fPaths.count(); ++i) {
+        canvas->save();
+        // position the path, and make it at off-integer coords.
+        canvas->translate(SK_Scalar1 * 200 * (i % 5) + SK_Scalar1 / 4,
+                          SK_Scalar1 * 200 * (i / 5) + 3 * SK_Scalar1 / 4);
+        SkColor color = rand.nextU();
+        color |= 0xff000000;
+        paint.setColor(color);
+        SkASSERT(fPaths[i].isConvex());
+        canvas->drawPath(fPaths[i], paint);
+        canvas->restore();
+    }
+    }
+    
+private:
+    typedef GM INHERITED;
+    SkTArray<SkPath> fPaths;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ConvexPathsGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
index 66692d1..0750dbe 100644 (file)
@@ -11,6 +11,7 @@
     '../gm/colormatrix.cpp',
     '../gm/complexclip.cpp',
     '../gm/complexclip2.cpp',
+    '../gm/convexpaths.cpp',
     '../gm/cubicpaths.cpp',
     '../gm/degeneratesegments.cpp',
     '../gm/drawbitmaprect.cpp',
index d16d797..c3f75f5 100644 (file)
 
         '../src/gpu/GrAAHairLinePathRenderer.cpp',
         '../src/gpu/GrAAHairLinePathRenderer.h',
-        '../src/gpu/GrAddPathRenderers_aahairline.cpp',
+        '../src/gpu/GrAAConvexPathRenderer.cpp',
+        '../src/gpu/GrAAConvexPathRenderer.h',
+        '../src/gpu/GrAddPathRenderers_default.cpp',
         '../src/gpu/GrAllocator.h',
         '../src/gpu/GrAllocPool.h',
         '../src/gpu/GrAllocPool.cpp',
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
new file mode 100644 (file)
index 0000000..7260962
--- /dev/null
@@ -0,0 +1,401 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrAAConvexPathRenderer.h"
+
+#include "GrContext.h"
+#include "GrDrawState.h"
+#include "GrPathUtils.h"
+#include "SkString.h"
+#include "SkTrace.h"
+
+
+GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
+}
+
+bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget::Caps& targetCaps,
+                                         const SkPath& path,
+                                         GrPathFill fill,
+                                         bool antiAlias) const {
+    return targetCaps.fShaderDerivativeSupport && antiAlias &&
+           kHairLine_PathFill != fill && !GrIsFillInverted(fill) &&
+           path.isConvex();
+}
+
+namespace {
+
+
+struct Segment {
+    enum {
+        kLine,
+        kQuad
+    } fType;
+    // line uses a, quad uses a and b (first point comes from prev. segment)
+    GrPoint fA, fB;
+    // normal to edge ending at a and b
+    GrVec fANorm, fBNorm;
+    // mid vector at a that splits angle with previous edge
+    GrVec fPrevMid;
+};
+
+typedef SkTArray<Segment, true> SegmentArray;
+
+bool is_path_degenerate(const GrPath& path) {
+    int n = path.countPoints();
+    if (n < 3) {
+        return true;
+    }
+
+    // compute a line from the first two points that are not equal, look for
+    // a third pt that is off the line.
+    static const SkScalar TOL = (SK_Scalar1 / 16);
+    bool foundLine = false;
+    GrPoint firstPoint = path.getPoint(0);
+    GrVec lineV;
+    SkScalar lineC;
+    int i = 1;
+
+    do {
+        GrPoint pt = path.getPoint(i);
+        if (!foundLine) {
+            if (pt != firstPoint) {
+                lineV = pt - firstPoint;
+                lineV.normalize();
+                lineV.setOrthog(lineV);
+                lineC = lineV.dot(firstPoint);
+                foundLine = true;
+            }
+        } else {
+            if (SkScalarAbs(lineV.dot(pt) - lineC) > TOL) {
+                return false;
+            }
+        }
+        ++i;
+    } while (i < n);
+    return true;
+}
+
+bool get_segments(const GrPath& path,
+                 SegmentArray* segments,
+                 int* quadCnt,
+                 int* lineCnt) {
+    *quadCnt = 0;
+    *lineCnt = 0;
+    SkPath::Iter iter(path, true);
+    // This renderer overemphasis very thin paths (every pixel intersected by
+    // the path will be at least 1/2 on). When the path degenerates to a line
+    // this makes the path draw as a hairline. This is a pretty glaring error
+    // so we detect this case and will not draw.
+    if (is_path_degenerate(path)) {
+        return false;
+    }
+    for (;;) {
+        GrPoint pts[4];
+        GrPathCmd cmd = (GrPathCmd)iter.next(pts);
+        switch (cmd) {
+            case kLine_PathCmd: {
+                segments->push_back();
+                segments->back().fType = Segment::kLine;
+                segments->back().fA = pts[1];
+                ++(*lineCnt);
+                break;
+            }
+            case kQuadratic_PathCmd:
+                segments->push_back();
+                segments->back().fType = Segment::kQuad;
+                segments->back().fA = pts[1];
+                segments->back().fB = pts[2];
+                ++(*quadCnt);
+                break;
+            case kCubic_PathCmd: {
+                SkSTArray<15, SkPoint, true> quads;
+                GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, &quads);
+                int count = quads.count();
+                for (int q = 0; q < count; q += 3) {
+                    segments->push_back();
+                    segments->back().fType = Segment::kQuad;
+                    segments->back().fA = quads[q + 1];
+                    segments->back().fB = quads[q + 2];
+                    ++(*quadCnt);
+                }
+                break;
+            };
+            case kEnd_PathCmd:
+                GrAssert(*quadCnt + *lineCnt == segments->count());
+                return true;
+            default:
+                break;
+        }
+    }
+}
+
+struct QuadVertex {
+    GrPoint  fPos;
+    union {
+        GrPoint fQuadUV;
+        GrScalar fEdge[4];
+    };
+};
+
+void get_counts(int quadCount, int lineCount, int* vCount, int* iCount) {
+    *vCount = 9 * lineCount + 11 * quadCount;
+    *iCount = 15  * lineCount + 24 * quadCount;
+}
+
+// for visual debugging, exagerate the AA smear at the edges
+// requires modifying the distance calc in the shader actually shade differently
+//#define STRETCH_AA
+#define STRETCH_FACTOR (20 * SK_Scalar1)
+
+void create_vertices(SegmentArray* segments,
+                     const GrPoint& fanPt,
+                     QuadVertex* verts,
+                     uint16_t* idxs) {
+    int count = segments->count();
+    GrAssert(count > 1);
+    int prevS = count - 1;
+    const Segment& lastSeg = (*segments)[prevS];
+
+    // walk the segments and compute normals to each edge and
+    // bisectors at vertices. The loop relies on having the end point and normal
+    // from previous segment so we first compute that. Also, we determine
+    // whether normals point left or right to face outside the path.
+    GrVec prevPt;
+    GrPoint prevPrevPt;
+    GrVec prevNorm;
+    if (Segment::kLine == lastSeg.fType) {
+        prevPt = lastSeg.fA;
+        const Segment& secondLastSeg = (*segments)[prevS - 1];
+        prevPrevPt = (Segment::kLine == secondLastSeg.fType) ?
+                                                      secondLastSeg.fA :
+                                                      secondLastSeg.fB;
+    } else {
+        prevPt = lastSeg.fB;
+        prevPrevPt = lastSeg.fA;
+    }
+    GrVec::Side outside;
+    // we will compute our edge vectors so that they are pointing along the
+    // direction in which we are iterating the path. So here we take an opposite
+    // vector and get the side that the fan pt lies relative to it.
+    fanPt.distanceToLineBetweenSqd(prevPrevPt, prevPt, &outside);
+    prevNorm = prevPt - prevPrevPt;
+    prevNorm.normalize();
+    prevNorm.setOrthog(prevNorm, outside);
+#ifdef STRETCH_AA
+    prevNorm.scale(STRETCH_FACTOR);
+#endif
+
+    // compute the normals and bisectors
+    for (int s = 0; s < count; ++s, ++prevS) {
+        Segment& curr = (*segments)[s];
+
+        GrVec currVec = curr.fA - prevPt;
+        currVec.normalize();
+        curr.fANorm.setOrthog(currVec, outside);
+#ifdef STRETCH_AA
+        curr.fANorm.scale(STRETCH_FACTOR);
+#endif
+        curr.fPrevMid = prevNorm + curr.fANorm;
+        curr.fPrevMid.normalize();
+#ifdef STRETCH_AA
+        curr.fPrevMid.scale(STRETCH_FACTOR);
+#endif
+        if (Segment::kLine == curr.fType) {
+            prevPt = curr.fA;
+            prevNorm = curr.fANorm;
+        } else {
+            currVec = curr.fB - curr.fA;
+            currVec.normalize();
+            curr.fBNorm.setOrthog(currVec, outside);
+#ifdef STRETCH_AA
+            curr.fBNorm.scale(STRETCH_FACTOR);
+#endif
+            prevPt = curr.fB;
+            prevNorm = curr.fBNorm;
+        }
+    }
+
+    // compute the vertices / indices
+    if (Segment::kLine == lastSeg.fType) {
+        prevPt = lastSeg.fA;
+        prevNorm = lastSeg.fANorm;
+    } else {
+        prevPt = lastSeg.fB;
+        prevNorm = lastSeg.fBNorm;
+    }
+    int v = 0;
+    int i = 0;
+    for (int s = 0; s < count; ++s, ++prevS) {
+        Segment& curr = (*segments)[s];
+        verts[v + 0].fPos = prevPt;
+        verts[v + 1].fPos = prevPt + prevNorm;
+        verts[v + 2].fPos = prevPt + curr.fPrevMid;
+        verts[v + 3].fPos = prevPt + curr.fANorm;
+        verts[v + 0].fQuadUV.set(0, 0);
+        verts[v + 1].fQuadUV.set(0, -SK_Scalar1);
+        verts[v + 2].fQuadUV.set(0, -SK_Scalar1);
+        verts[v + 3].fQuadUV.set(0, -SK_Scalar1);
+
+        idxs[i + 0] = v + 0;
+        idxs[i + 1] = v + 1;
+        idxs[i + 2] = v + 2;
+        idxs[i + 3] = v + 0;
+        idxs[i + 4] = v + 2;
+        idxs[i + 5] = v + 3;
+
+        v += 4;
+        i += 6;
+
+        if (Segment::kLine == curr.fType) {
+            verts[v + 0].fPos = fanPt;
+            verts[v + 1].fPos = prevPt;
+            verts[v + 2].fPos = curr.fA;
+            verts[v + 3].fPos = prevPt + curr.fANorm;
+            verts[v + 4].fPos = curr.fA + curr.fANorm;
+            GrScalar lineC = -curr.fANorm.dot(curr.fA);
+            GrScalar fanDist = curr.fANorm.dot(fanPt) - lineC;
+            verts[v + 0].fQuadUV.set(0, SkScalarAbs(fanDist));
+            verts[v + 1].fQuadUV.set(0, 0);
+            verts[v + 2].fQuadUV.set(0, 0);
+            verts[v + 3].fQuadUV.set(0, -GR_Scalar1);
+            verts[v + 4].fQuadUV.set(0, -GR_Scalar1);
+
+            idxs[i + 0] = v + 0;
+            idxs[i + 1] = v + 1;
+            idxs[i + 2] = v + 2;
+            idxs[i + 3] = v + 1;
+            idxs[i + 4] = v + 3;
+            idxs[i + 5] = v + 4;
+            idxs[i + 6] = v + 1;
+            idxs[i + 7] = v + 4;
+            idxs[i + 8] = v + 2;
+
+            i += 9;
+            v += 5;
+
+            prevPt = curr.fA;
+            prevNorm = curr.fANorm;
+        } else {
+            GrVec splitVec = curr.fANorm + curr.fBNorm;
+            splitVec.normalize();
+#ifdef STRETCH_AA
+            splitVec.scale(STRETCH_FACTOR);
+#endif
+
+            verts[v + 0].fPos = prevPt;
+            verts[v + 1].fPos = curr.fA;
+            verts[v + 2].fPos = curr.fB;
+            verts[v + 3].fPos = fanPt;
+            verts[v + 4].fPos = prevPt + curr.fANorm;
+            verts[v + 5].fPos = curr.fA + splitVec;
+            verts[v + 6].fPos = curr.fB + curr.fBNorm;
+
+            verts[v + 0].fQuadUV.set(0, 0);
+            verts[v + 1].fQuadUV.set(GR_ScalarHalf, 0);
+            verts[v + 2].fQuadUV.set(GR_Scalar1, GR_Scalar1);
+            GrMatrix toUV;
+            GrPoint pts[] = {prevPt, curr.fA, curr.fB};
+            GrPathUtils::quadDesignSpaceToUVCoordsMatrix(pts, &toUV);
+            toUV.mapPointsWithStride(&verts[v + 3].fQuadUV,
+                                     &verts[v + 3].fPos,
+                                     sizeof(QuadVertex), 4);
+
+            idxs[i +  0] = v + 3;
+            idxs[i +  1] = v + 0;
+            idxs[i +  2] = v + 1;
+            idxs[i +  3] = v + 3;
+            idxs[i +  4] = v + 1;
+            idxs[i +  5] = v + 2;
+            idxs[i +  6] = v + 0;
+            idxs[i +  7] = v + 4;
+            idxs[i +  8] = v + 1;
+            idxs[i +  9] = v + 4;
+            idxs[i + 10] = v + 1;
+            idxs[i + 11] = v + 5;
+            idxs[i + 12] = v + 5;
+            idxs[i + 13] = v + 1;
+            idxs[i + 14] = v + 2;
+            idxs[i + 15] = v + 5;
+            idxs[i + 16] = v + 2;
+            idxs[i + 17] = v + 6;
+
+            i += 18;
+            v += 7;
+            prevPt = curr.fB;
+            prevNorm = curr.fBNorm;
+        }
+    }
+}
+
+}
+
+void GrAAConvexPathRenderer::drawPath(GrDrawState::StageMask stageMask) {
+    GrAssert(fPath->isConvex());
+    if (fPath->isEmpty()) {
+        return;
+    }
+    GrDrawState* drawState = fTarget->drawState();
+
+    GrDrawTarget::AutoStateRestore asr;
+    GrMatrix vm = drawState->getViewMatrix();
+    vm.postTranslate(fTranslate.fX, fTranslate.fY);
+    asr.set(fTarget);
+    GrMatrix ivm;
+    if (vm.invert(&ivm)) {
+        drawState->preConcatSamplerMatrices(stageMask, ivm);
+    }
+    drawState->setViewMatrix(GrMatrix::I());
+
+
+    SkPath path;
+    fPath->transform(vm, &path);
+
+    SkPoint fanPt = {path.getBounds().centerX(),
+                     path.getBounds().centerY()};
+
+    GrVertexLayout layout = 0;
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        if ((1 << s) & stageMask) {
+            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
+        }
+    }
+    layout |= GrDrawTarget::kEdge_VertexLayoutBit;
+
+    QuadVertex *verts;
+    uint16_t* idxs;
+
+    int nQuads;
+    int nLines;
+    SegmentArray segments;
+    if (!get_segments(path, &segments, &nQuads, &nLines)) {
+        return;
+    }
+    int vCount;
+    int iCount;
+    get_counts(nQuads, nLines, &vCount, &iCount);
+
+    if (!fTarget->reserveVertexSpace(layout,
+                                     vCount,
+                                     reinterpret_cast<void**>(&verts))) {
+        return;
+    }
+    if (!fTarget->reserveIndexSpace(iCount, reinterpret_cast<void**>(&idxs))) {
+        fTarget->resetVertexSource();
+        return;
+    }
+
+    create_vertices(&segments, fanPt, verts, idxs);
+
+    drawState->setVertexEdgeType(GrDrawState::kQuad_EdgeType);
+    fTarget->drawIndexed(kTriangles_PrimitiveType,
+                         0,        // start vertex
+                         0,        // start index
+                         vCount,
+                         iCount);
+}
+
diff --git a/src/gpu/GrAAConvexPathRenderer.h b/src/gpu/GrAAConvexPathRenderer.h
new file mode 100644 (file)
index 0000000..dff06c6
--- /dev/null
@@ -0,0 +1,20 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrPathRenderer.h"
+
+
+class GrAAConvexPathRenderer : public GrPathRenderer {
+public:
+    GrAAConvexPathRenderer();
+    bool canDrawPath(const GrDrawTarget::Caps& targetCaps,
+                                       const SkPath& path,
+                                       GrPathFill fill,
+                                       bool antiAlias) const;
+    void drawPath(GrDrawState::StageMask stageMask);
+};
index 29db9aa..b78be5f 100644 (file)
@@ -140,65 +140,6 @@ typedef SkTArray<SkPoint, true> PtArray;
 #define PREALLOC_PTARRAY(N) SkSTArray<(N),SkPoint, true>
 typedef SkTArray<int, true> IntArray;
 
-/**
- * We convert cubics to quadratics (for now).
- */
-void convert_noninflect_cubic_to_quads(const SkPoint p[4],
-                                       SkScalar tolScale,
-                                       PtArray* quads,
-                                       int sublevel = 0) {
-    SkVector ab = p[1];
-    ab -= p[0];
-    SkVector dc = p[2];
-    dc -= p[3];
-
-    static const SkScalar gLengthScale = 3 * SK_Scalar1 / 2;
-    // base tolerance is 2 pixels in dev coords.
-    const SkScalar distanceSqdTol = SkScalarMul(tolScale, 2 * SK_Scalar1);
-    static const int kMaxSubdivs = 10;
-
-    ab.scale(gLengthScale);
-    dc.scale(gLengthScale);
-
-    SkVector c0 = p[0];
-    c0 += ab;
-    SkVector c1 = p[3];
-    c1 += dc;
-
-    SkScalar dSqd = c0.distanceToSqd(c1);
-    if (sublevel > kMaxSubdivs || dSqd <= distanceSqdTol) {
-        SkPoint cAvg = c0;
-        cAvg += c1;
-        cAvg.scale(SK_ScalarHalf);
-
-        SkPoint* pts = quads->push_back_n(3);
-        pts[0] = p[0];
-        pts[1] = cAvg;
-        pts[2] = p[3];
-
-        return;
-    } else {
-        SkPoint choppedPts[7];
-        SkChopCubicAtHalf(p, choppedPts);
-        convert_noninflect_cubic_to_quads(choppedPts + 0, tolScale, 
-                                          quads, sublevel + 1);
-        convert_noninflect_cubic_to_quads(choppedPts + 3, tolScale,
-                                          quads, sublevel + 1);
-    }
-}
-
-void convert_cubic_to_quads(const SkPoint p[4],
-                            SkScalar tolScale,
-                            PtArray* quads) {
-    SkPoint chopped[13];
-    int count = SkChopCubicAtInflections(p, chopped);
-
-    for (int i = 0; i < count; ++i) {
-        SkPoint* cubic = chopped + 3*i;
-        convert_noninflect_cubic_to_quads(cubic, tolScale, quads);
-    }
-}
-
 // Takes 178th time of logf on Z600 / VC2010
 int get_float_exp(float x) {
     GR_STATIC_ASSERT(sizeof(int) == sizeof(float));
@@ -350,14 +291,15 @@ int generate_lines_and_quads(const SkPath& path,
                 bounds.roundOut(&ibounds);
                 if (SkIRect::Intersects(clip, ibounds)) {
                     PREALLOC_PTARRAY(32) q;
-                    // in perspective have to do conversion in src space
+                    // We convert cubics to quadratics (for now).
+                    // In perspective have to do conversion in src space.
                     if (persp) {
                         SkScalar tolScale = 
                             GrPathUtils::scaleToleranceToSrc(SK_Scalar1, m,
                                                              path.getBounds());
-                        convert_cubic_to_quads(pts, tolScale, &q);
+                        GrPathUtils::convertCubicToQuads(pts, tolScale, &q);
                     } else {
-                        convert_cubic_to_quads(devPts, SK_Scalar1, &q);
+                        GrPathUtils::convertCubicToQuads(devPts, SK_Scalar1, &q);
                     }
                     for (int i = 0; i < q.count(); i += 3) {
                         SkPoint* qInDevSpace;
@@ -447,24 +389,9 @@ void bloat_quad(const SkPoint qpts[3], const GrMatrix* toDevice,
     SkPoint b = qpts[1];
     SkPoint c = qpts[2];
 
-    // compute a matrix that goes from device coords to U,V quad params
     // this should be in the src space, not dev coords, when we have perspective
     SkMatrix DevToUV;
-    DevToUV.setAll(a.fX,           b.fX,          c.fX,
-                   a.fY,           b.fY,          c.fY,
-                   SK_Scalar1,     SK_Scalar1,    SK_Scalar1);
-    DevToUV.invert(&DevToUV);
-    // can't make this static, no cons :(
-    SkMatrix UVpts;
-    UVpts.setAll(0,                 SK_ScalarHalf,  SK_Scalar1,
-                 0,                 0,              SK_Scalar1,
-                 SK_Scalar1,        SK_Scalar1,     SK_Scalar1);
-    DevToUV.postConcat(UVpts);
-
-    // We really want to avoid perspective matrix muls.
-    // These may wind up really close to zero
-    DevToUV.setPerspX(0);
-    DevToUV.setPerspY(0);
+    GrPathUtils::quadDesignSpaceToUVCoordsMatrix(qpts, &DevToUV);
 
     if (toDevice) {
         toDevice->mapPoints(&a, 1);
similarity index 63%
rename from src/gpu/GrAddPathRenderers_aahairline.cpp
rename to src/gpu/GrAddPathRenderers_default.cpp
index a7df66e..37c388e 100644 (file)
@@ -1,20 +1,26 @@
 
 /*
- * Copyright 2011 Google Inc.
+ * Copyright 2012 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
 
-
 #include "GrAAHairLinePathRenderer.h"
+#include "GrAAConvexPathRenderer.h"
 
 void GrPathRenderer::AddPathRenderers(GrContext* ctx,
                                       GrPathRendererChain::UsageFlags flags,
                                       GrPathRendererChain* chain) {
     if (!(GrPathRendererChain::kNonAAOnly_UsageFlag & flags)) {
+
         if (GrPathRenderer* pr = GrAAHairLinePathRenderer::Create(ctx)) {
             chain->addPathRenderer(pr)->unref();
         }
+        // Disabled for now. Need to fix issue where some hairlines don't
+        // wind up going to the hairline renderer and get rendered by this
+        // PR looking speckly.
+        //chain->addPathRenderer(new GrAAConvexPathRenderer())->unref();
     }
 }
index 4191c4a..51443cb 100644 (file)
@@ -489,9 +489,15 @@ struct GrDrawState {
         /* 1-pixel wide line
            2D implicit line eq (a*x + b*y +c = 0). 4th component unused */
         kHairLine_EdgeType,
-        /* 1-pixel wide quadratic
-           u^2-v canonical coords (only 2 components used) */
-        kHairQuad_EdgeType
+        /* Quadratic specified by u^2-v canonical coords (only 2 
+           components used). Coverage based on signed distance with negative
+           being inside, positive outside.*/
+        kQuad_EdgeType,
+        /* Same as above but for hairline quadratics. Uses unsigned distance.
+           Coverage is min(0, 1-distance). */
+        kHairQuad_EdgeType,
+
+        kVertexEdgeTypeCnt
     };
 
     /**
@@ -500,6 +506,7 @@ struct GrDrawState {
      * are not specified the value of this setting has no effect.
      */
     void setVertexEdgeType(VertexEdgeType type) {
+        GrAssert(type >=0 && type < kVertexEdgeTypeCnt);
         fVertexEdgeType = type;
     }
 
index 2e391e3..d159f5a 100644 (file)
@@ -526,21 +526,28 @@ void GrGLProgram::genEdgeCoverage(const GrGLInterface* gl,
         segments->fVSCode.appendf("\t%s = " EDGE_ATTR_NAME ";\n", vsName);
         if (GrDrawState::kHairLine_EdgeType == fProgramDesc.fVertexEdgeType) {
             segments->fFSCode.appendf("\tfloat edgeAlpha = abs(dot(vec3(gl_FragCoord.xy,1), %s.xyz));\n", fsName);
+            segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
         } else {
-            GrAssert(GrDrawState::kHairQuad_EdgeType == fProgramDesc.fVertexEdgeType);
+            GrAssert(GrDrawState::kHairQuad_EdgeType == fProgramDesc.fVertexEdgeType ||
+                     GrDrawState::kQuad_EdgeType == fProgramDesc.fVertexEdgeType);
             // for now we know we're not in perspective, so we could compute this
             // per-quadratic rather than per pixel
             segments->fFSCode.appendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
             segments->fFSCode.appendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
-            segments->fFSCode.appendf("\tfloat dfdx = 2.0*%s.x*duvdx.x - duvdx.y;\n", fsName);
-            segments->fFSCode.appendf("\tfloat dfdy = 2.0*%s.x*duvdy.x - duvdy.y;\n", fsName);
+            segments->fFSCode.appendf("\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
+                                      "\t               2.0*%s.x*duvdy.x - duvdy.y);\n",
+                                      fsName, fsName);
             segments->fFSCode.appendf("\tfloat edgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName);
-            segments->fFSCode.append("\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / (dfdx*dfdx + dfdy*dfdy));\n");
+            if (GrDrawState::kQuad_EdgeType == fProgramDesc.fVertexEdgeType) {
+                segments->fFSCode.append("\tedgeAlpha = clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);\n");
+            } else {
+                segments->fFSCode.append("\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / dot(gF, gF));\n");
+                segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
+            }
             if (gl->supportsES2()) {
                 segments->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
             }
         }
-        segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
         *coverageVar = "edgeAlpha";
     } else {
         coverageVar->reset();
index 2398149..251fc41 100644 (file)
@@ -219,9 +219,7 @@ bool GrGpuGLShaders::programUnitTest() {
             if (vertexEdgeAA) {
                 pdesc.fVertexLayout |= GrDrawTarget::kEdge_VertexLayoutBit;
                 if (this->getCaps().fShaderDerivativeSupport) {
-                    pdesc.fVertexEdgeType = random_bool(&random) ?
-                        GrDrawState::kHairQuad_EdgeType :
-                        GrDrawState::kHairLine_EdgeType;
+                    pdesc.fVertexEdgeType = (GrDrawState::VertexEdgeType) random_int(&random, GrDrawState::kVertexEdgeTypeCnt);
                 } else {
                     pdesc.fVertexEdgeType = GrDrawState::kHairLine_EdgeType;
                 }
index 0a7759d..e41de5c 100644 (file)
@@ -8,8 +8,8 @@
 
 
 #include "GrPathUtils.h"
-
 #include "GrPoint.h"
+#include "SkGeometry.h"
 
 GrScalar GrPathUtils::scaleToleranceToSrc(GrScalar devTol,
                                           const GrMatrix& viewM,
@@ -186,3 +186,107 @@ int GrPathUtils::worstCasePointCount(const GrPath& path, int* subpaths,
     }
     return pointCount;
 }
+
+namespace {
+// The matrix computed for quadDesignSpaceToUVCoordsMatrix should never really
+// have perspective and we really want to avoid perspective matrix muls.
+//  However, the first two entries of the perspective row may be really close to
+// 0 and the third may not be 1 due to a scale on the entire matrix.
+inline void fixup_matrix(GrMatrix* mat) {
+    GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
+    static const GrScalar gTOL = 1.f / 100.f;
+    GrAssert(GrScalarAbs(mat->get(SkMatrix::kMPersp0)) < gTOL);
+    GrAssert(GrScalarAbs(mat->get(SkMatrix::kMPersp1)) < gTOL);
+    float m33 = mat->get(SkMatrix::kMPersp2);
+    if (1.f != m33) {
+        m33 = 1.f / m33;
+        mat->setAll(m33 * mat->get(SkMatrix::kMScaleX),
+                    m33 * mat->get(SkMatrix::kMSkewX),
+                    m33 * mat->get(SkMatrix::kMTransX),
+                    m33 * mat->get(SkMatrix::kMSkewY),
+                    m33 * mat->get(SkMatrix::kMScaleY),
+                    m33 * mat->get(SkMatrix::kMTransY),
+                    0.f, 0.f, 1.f);
+    } else {
+        mat->setPerspX(0);
+        mat->setPerspY(0);
+    }
+}
+}
+
+// Compute a matrix that goes from the 2d space coordinates to UV space where
+// u^2-v = 0 specifies the quad.
+void GrPathUtils::quadDesignSpaceToUVCoordsMatrix(const SkPoint qPts[3],
+                                                  GrMatrix* matrix) {
+    // can't make this static, no cons :(
+    SkMatrix UVpts;
+    GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
+    UVpts.setAll(0,   0.5f,  1.f,
+                 0,   0,     1.f,
+                 1.f, 1.f,   1.f);
+    matrix->setAll(qPts[0].fX, qPts[1].fX, qPts[2].fX,
+                   qPts[0].fY, qPts[1].fY, qPts[2].fY,
+                   1.f,        1.f,        1.f);
+    matrix->invert(matrix);
+    matrix->postConcat(UVpts);
+    fixup_matrix(matrix);
+}
+
+namespace {
+void convert_noninflect_cubic_to_quads(const SkPoint p[4],
+                                       SkScalar tolScale,
+                                       SkTArray<SkPoint, true>* quads,
+                                       int sublevel = 0) {
+    SkVector ab = p[1];
+    ab -= p[0];
+    SkVector dc = p[2];
+    dc -= p[3];
+
+    static const SkScalar gLengthScale = 3 * SK_Scalar1 / 2;
+    // base tolerance is 2 pixels in dev coords.
+    const SkScalar distanceSqdTol = SkScalarMul(tolScale, 1 * SK_Scalar1);
+    static const int kMaxSubdivs = 10;
+
+    ab.scale(gLengthScale);
+    dc.scale(gLengthScale);
+
+    SkVector c0 = p[0];
+    c0 += ab;
+    SkVector c1 = p[3];
+    c1 += dc;
+
+    SkScalar dSqd = c0.distanceToSqd(c1);
+    if (sublevel > kMaxSubdivs || dSqd <= distanceSqdTol) {
+        SkPoint cAvg = c0;
+        cAvg += c1;
+        cAvg.scale(SK_ScalarHalf);
+
+        SkPoint* pts = quads->push_back_n(3);
+        pts[0] = p[0];
+        pts[1] = cAvg;
+        pts[2] = p[3];
+
+        return;
+    } else {
+        SkPoint choppedPts[7];
+        SkChopCubicAtHalf(p, choppedPts);
+        convert_noninflect_cubic_to_quads(choppedPts + 0, tolScale, 
+                                          quads, sublevel + 1);
+        convert_noninflect_cubic_to_quads(choppedPts + 3, tolScale,
+                                          quads, sublevel + 1);
+    }
+}
+}
+
+void GrPathUtils::convertCubicToQuads(const GrPoint p[4],
+                                      SkScalar tolScale,
+                                      SkTArray<SkPoint, true>* quads) {
+    SkPoint chopped[10];
+    int count = SkChopCubicAtInflections(p, chopped);
+
+    for (int i = 0; i < count; ++i) {
+        SkPoint* cubic = chopped + 3*i;
+        convert_noninflect_cubic_to_quads(cubic, tolScale, quads);
+    }
+
+}
index 5dc06aa..df2e16c 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "GrMatrix.h"
 #include "GrPath.h"
+#include "SkTArray.h"
 
 /**
  *  Utilities for evaluating paths.
@@ -45,6 +46,16 @@ namespace GrPathUtils {
                                  GrScalar tolSqd,
                                  GrPoint** points,
                                  uint32_t pointsLeft);
-
+    // Compute a matrix that goes from the 2d space coordinates to UV space
+    // where u^2-v = 0 specifies the quad.
+    void quadDesignSpaceToUVCoordsMatrix(const GrPoint qPts[3],
+                                         GrMatrix* matrix);
+    // Converts a cubic into a sequence of quads. If working in device space
+    // use tolScale = 1, otherwise set based on stretchiness of the matrix. The
+    // result is sets of 3 points in quads (TODO: share endpoints in returned
+    // array)
+    void convertCubicToQuads(const GrPoint p[4],
+                             SkScalar tolScale,
+                             SkTArray<SkPoint, true>* quads);
 };
 #endif