From 69cc6ad20ed03f35f9d3c8119a2c32187669a22b Mon Sep 17 00:00:00 2001 From: "bsalomon@google.com" Date: Tue, 17 Jan 2012 14:25:10 +0000 Subject: [PATCH] Add convex path renderer (disabled) Review URL: http://codereview.appspot.com/5533061/ git-svn-id: http://skia.googlecode.com/svn/trunk@3040 2bbb7eff-a529-9590-31e7-b0007b416f81 --- gm/convexpaths.cpp | 136 +++++++ gyp/gmslides.gypi | 1 + gyp/gpu.gyp | 4 +- src/gpu/GrAAConvexPathRenderer.cpp | 401 +++++++++++++++++++++ src/gpu/GrAAConvexPathRenderer.h | 20 + src/gpu/GrAAHairLinePathRenderer.cpp | 83 +---- ...hairline.cpp => GrAddPathRenderers_default.cpp} | 10 +- src/gpu/GrDrawState.h | 13 +- src/gpu/GrGLProgram.cpp | 17 +- src/gpu/GrGpuGLShaders.cpp | 4 +- src/gpu/GrPathUtils.cpp | 106 +++++- src/gpu/GrPathUtils.h | 13 +- 12 files changed, 714 insertions(+), 94 deletions(-) create mode 100644 gm/convexpaths.cpp create mode 100644 src/gpu/GrAAConvexPathRenderer.cpp create mode 100644 src/gpu/GrAAConvexPathRenderer.h rename src/gpu/{GrAddPathRenderers_aahairline.cpp => GrAddPathRenderers_default.cpp} (63%) diff --git a/gm/convexpaths.cpp b/gm/convexpaths.cpp new file mode 100644 index 0000000..a7e3119 --- /dev/null +++ b/gm/convexpaths.cpp @@ -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 fPaths; +}; + +////////////////////////////////////////////////////////////////////////////// + +static GM* MyFactory(void*) { return new ConvexPathsGM; } +static GMRegistry reg(MyFactory); + +} + diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index 66692d1..0750dbe 100644 --- a/gyp/gmslides.gypi +++ b/gyp/gmslides.gypi @@ -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', diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp index d16d797..c3f75f5 100644 --- a/gyp/gpu.gyp +++ b/gyp/gpu.gyp @@ -186,7 +186,9 @@ '../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 index 0000000..7260962 --- /dev/null +++ b/src/gpu/GrAAConvexPathRenderer.cpp @@ -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 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(&verts))) { + return; + } + if (!fTarget->reserveIndexSpace(iCount, reinterpret_cast(&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 index 0000000..dff06c6 --- /dev/null +++ b/src/gpu/GrAAConvexPathRenderer.h @@ -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); +}; diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp index 29db9aa..b78be5f 100644 --- a/src/gpu/GrAAHairLinePathRenderer.cpp +++ b/src/gpu/GrAAHairLinePathRenderer.cpp @@ -140,65 +140,6 @@ typedef SkTArray PtArray; #define PREALLOC_PTARRAY(N) SkSTArray<(N),SkPoint, true> typedef SkTArray 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); diff --git a/src/gpu/GrAddPathRenderers_aahairline.cpp b/src/gpu/GrAddPathRenderers_default.cpp similarity index 63% rename from src/gpu/GrAddPathRenderers_aahairline.cpp rename to src/gpu/GrAddPathRenderers_default.cpp index a7df66e..37c388e 100644 --- a/src/gpu/GrAddPathRenderers_aahairline.cpp +++ b/src/gpu/GrAddPathRenderers_default.cpp @@ -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(); } } diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h index 4191c4a..51443cb 100644 --- a/src/gpu/GrDrawState.h +++ b/src/gpu/GrDrawState.h @@ -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; } diff --git a/src/gpu/GrGLProgram.cpp b/src/gpu/GrGLProgram.cpp index 2e391e3..d159f5a 100644 --- a/src/gpu/GrGLProgram.cpp +++ b/src/gpu/GrGLProgram.cpp @@ -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(); diff --git a/src/gpu/GrGpuGLShaders.cpp b/src/gpu/GrGpuGLShaders.cpp index 2398149..251fc41 100644 --- a/src/gpu/GrGpuGLShaders.cpp +++ b/src/gpu/GrGpuGLShaders.cpp @@ -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; } diff --git a/src/gpu/GrPathUtils.cpp b/src/gpu/GrPathUtils.cpp index 0a7759d..e41de5c 100644 --- a/src/gpu/GrPathUtils.cpp +++ b/src/gpu/GrPathUtils.cpp @@ -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* 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* 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); + } + +} diff --git a/src/gpu/GrPathUtils.h b/src/gpu/GrPathUtils.h index 5dc06aa..df2e16c 100644 --- a/src/gpu/GrPathUtils.h +++ b/src/gpu/GrPathUtils.h @@ -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* quads); }; #endif -- 2.7.4