--- /dev/null
+
+/*
+ * 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);
+
+}
+
'../gm/colormatrix.cpp',
'../gm/complexclip.cpp',
'../gm/complexclip2.cpp',
+ '../gm/convexpaths.cpp',
'../gm/cubicpaths.cpp',
'../gm/degeneratesegments.cpp',
'../gm/drawbitmaprect.cpp',
'../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',
--- /dev/null
+
+/*
+ * 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);
+}
+
--- /dev/null
+
+/*
+ * 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);
+};
#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));
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;
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);
/*
- * 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();
}
}
/* 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
};
/**
* are not specified the value of this setting has no effect.
*/
void setVertexEdgeType(VertexEdgeType type) {
+ GrAssert(type >=0 && type < kVertexEdgeTypeCnt);
fVertexEdgeType = type;
}
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();
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;
}
#include "GrPathUtils.h"
-
#include "GrPoint.h"
+#include "SkGeometry.h"
GrScalar GrPathUtils::scaleToleranceToSrc(GrScalar devTol,
const GrMatrix& viewM,
}
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);
+ }
+
+}
#include "GrMatrix.h"
#include "GrPath.h"
+#include "SkTArray.h"
/**
* Utilities for evaluating paths.
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