Add GrAAHairLinePathRenderer

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



git-svn-id: http://skia.googlecode.com/svn/trunk@2196 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
bsalomon@google.com 2011-08-30 18:13:44 +00:00
parent dec9f2d351
commit aeb2160b1d
18 changed files with 1082 additions and 47 deletions

View File

@ -0,0 +1,633 @@
#include "GrAAHairLinePathRenderer.h"
#include "GrContext.h"
#include "GrGpu.h"
#include "GrIndexBuffer.h"
#include "SkGeometry.h"
#include "SkTemplates.h"
namespace {
// quadratics are rendered as 5-sided polys in order to bound the
// AA stroke around the center-curve. See comments in push_quad_index_buffer and
// bloat_quad.
static const int kVertsPerQuad = 5;
static const int kIdxsPerQuad = 9;
static const int kVertsPerLineSeg = 4;
static const int kIdxsPerLineSeg = 6;
static const int kNumQuadsInIdxBuffer = 256;
static const size_t kQuadIdxSBufize = kIdxsPerQuad *
sizeof(uint16_t) *
kNumQuadsInIdxBuffer;
bool push_quad_index_data(GrIndexBuffer* qIdxBuffer) {
uint16_t* data = (uint16_t*) qIdxBuffer->lock();
bool tempData = NULL == data;
if (tempData) {
data = new uint16_t[kNumQuadsInIdxBuffer * kIdxsPerQuad];
}
for (int i = 0; i < kNumQuadsInIdxBuffer; ++i) {
// Each quadratic is rendered as a five sided polygon. This poly bounds
// the quadratic's bounding triangle but has been expanded so that the
// 1-pixel wide area around the curve is inside the poly.
// If a,b,c are the original control points then the poly a0,b0,c0,c1,a1
// that is rendered would look like this:
// b0
// b
//
// a0 c0
// a c
// a1 c1
// Each is drawn as three triagnles specified by these 9 indices:
int baseIdx = i * kIdxsPerQuad;
uint16_t baseVert = (uint16_t)(i * kVertsPerQuad);
data[0 + baseIdx] = baseVert + 0; // a0
data[1 + baseIdx] = baseVert + 1; // a1
data[2 + baseIdx] = baseVert + 2; // b0
data[3 + baseIdx] = baseVert + 2; // b0
data[4 + baseIdx] = baseVert + 4; // c1
data[5 + baseIdx] = baseVert + 3; // c0
data[6 + baseIdx] = baseVert + 1; // a1
data[7 + baseIdx] = baseVert + 4; // c1
data[8 + baseIdx] = baseVert + 2; // b0
}
if (tempData) {
bool ret = qIdxBuffer->updateData(data, kQuadIdxSBufize);
delete[] data;
return ret;
} else {
qIdxBuffer->unlock();
return true;
}
}
}
GrPathRenderer* GrAAHairLinePathRenderer::Create(GrContext* context) {
if (CanBeUsed(context)) {
const GrIndexBuffer* lIdxBuffer = context->getQuadIndexBuffer();
if (NULL == lIdxBuffer) {
return NULL;
}
GrGpu* gpu = context->getGpu();
GrIndexBuffer* qIdxBuf = gpu->createIndexBuffer(kQuadIdxSBufize, false);
SkAutoTUnref<GrIndexBuffer> qIdxBuffer(qIdxBuf); // cons will take a ref
if (NULL == qIdxBuf ||
!push_quad_index_data(qIdxBuffer.get())) {
return NULL;
}
return new GrAAHairLinePathRenderer(context,
lIdxBuffer,
qIdxBuf);
} else {
return NULL;
}
}
bool GrAAHairLinePathRenderer::CanBeUsed(const GrContext* context) {
return context->getGpu()->supportsShaderDerivatives();
}
GrAAHairLinePathRenderer::GrAAHairLinePathRenderer(
const GrContext* context,
const GrIndexBuffer* linesIndexBuffer,
const GrIndexBuffer* quadsIndexBuffer) {
GrAssert(CanBeUsed(context));
fLinesIndexBuffer = linesIndexBuffer;
linesIndexBuffer->ref();
fQuadsIndexBuffer = quadsIndexBuffer;
quadsIndexBuffer->ref();
this->resetGeom();
}
GrAAHairLinePathRenderer::~GrAAHairLinePathRenderer() {
fLinesIndexBuffer->unref();
fQuadsIndexBuffer->unref();
}
bool GrAAHairLinePathRenderer::supportsAA(GrDrawTarget* target,
const SkPath& path,
GrPathFill fill) {
return kHairLine_PathFill == fill;
}
bool GrAAHairLinePathRenderer::canDrawPath(const GrDrawTarget* target,
const SkPath& path,
GrPathFill fill) const {
// TODO: support perspective
return kHairLine_PathFill == fill &&
!target->getViewMatrix().hasPerspective();
}
void GrAAHairLinePathRenderer::pathWillClear() {
this->resetGeom();
}
void GrAAHairLinePathRenderer::resetGeom() {
fPreviousStages = ~0;
fPreviousRTHeight = ~0;
fPreviousViewMatrix = GrMatrix::InvalidMatrix();
fLineSegmentCnt = 0;
fQuadCnt = 0;
if ((fQuadCnt || fLineSegmentCnt) && NULL != fTarget) {
fTarget->resetVertexSource();
}
}
namespace {
typedef GrTArray<SkPoint, true> PtArray;
typedef GrTArray<int, true> IntArray;
/**
* We convert cubics to quadratics (for now).
*/
void convert_noninflect_cubic_to_quads(const SkPoint p[4],
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;
static const SkScalar gDistanceSqdTol = 2 * SK_Scalar1;
static const int kMaxSubdivs = 30;
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 <= gDistanceSqdTol) {
SkPoint cAvg = c0;
cAvg += c1;
cAvg.scale(SK_ScalarHalf);
int idx = quads->count();
quads->push_back_n(3);
(*quads)[idx+0] = p[0];
(*quads)[idx+1] = cAvg;
(*quads)[idx+2] = p[3];
return;
} else {
SkPoint choppedPts[7];
SkChopCubicAtHalf(p, choppedPts);
convert_noninflect_cubic_to_quads(choppedPts + 0, quads, sublevel + 1);
convert_noninflect_cubic_to_quads(choppedPts + 3, quads, sublevel + 1);
}
}
void convert_cubic_to_quads(const SkPoint p[4], 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, quads);
}
}
// Takes 178th time of logf on Z600 / VC2010
int get_float_exp(float x) {
GR_STATIC_ASSERT(sizeof(int) == sizeof(float));
#if GR_DEBUG
static bool tested;
if (!tested) {
tested = true;
GrAssert(get_float_exp(0.25f) == -2);
GrAssert(get_float_exp(0.3f) == -2);
GrAssert(get_float_exp(0.5f) == -1);
GrAssert(get_float_exp(1.f) == 0);
GrAssert(get_float_exp(2.f) == 1);
GrAssert(get_float_exp(2.5f) == 1);
GrAssert(get_float_exp(8.f) == 3);
GrAssert(get_float_exp(100.f) == 6);
GrAssert(get_float_exp(1000.f) == 9);
GrAssert(get_float_exp(1024.f) == 10);
GrAssert(get_float_exp(3000000.f) == 21);
}
#endif
return (((*(int*)&x) & 0x7f800000) >> 23) - 127;
}
// we subdivide the quads to avoid huge overfill
// if it returns -1 then should be drawn as lines
int num_quad_subdivs(const SkPoint p[3]) {
static const SkScalar gDegenerateToLineTol = SK_Scalar1;
GrScalar dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]);
if (dsqd < gDegenerateToLineTol*gDegenerateToLineTol) {
return -1;
}
if (p[2].distanceToLineBetweenSqd(p[1],p[0]) <
gDegenerateToLineTol*gDegenerateToLineTol) {
return -1;
}
static const int kMaxSub = 4;
// tolerance of triangle height in pixels
// tuned on windows Quadro FX 380 / Z600
// trade off of fill vs cpu time on verts
// maybe different when do this using gpu (geo or tess shaders)
static const SkScalar gSubdivTol = 175 * SK_Scalar1;
if (dsqd <= gSubdivTol*gSubdivTol) {
return 0;
} else {
// subdividing the quad reduces d by 4. so we want x = log4(d/tol)
// = log4(d*d/tol*tol)/2
// = log2(d*d/tol*tol)
#ifdef SK_SCALAR_IS_FLOAT
// +1 since we're ignoring the mantissa contribution.
int log = get_float_exp(dsqd/(gSubdivTol*gSubdivTol)) + 1;
log = GrMin(GrMax(0, log), kMaxSub);
return log;
#else
SkScalar log = SkScalarLog(SkScalarDiv(dsqd,kTol*kTol));
static const SkScalar conv = SkScalarInvert(SkScalarLog(2));
log = SkScalarMul(log, conv);
return GrMin(GrMax(0, SkScalarCeilToInt(log)),kMaxSub);
#endif
}
}
int get_lines_and_quads(const SkPath& path, const SkMatrix& m, GrIRect clip,
PtArray* lines, PtArray* quads,
IntArray* quadSubdivCnts) {
SkPath::Iter iter(path, false);
int totalQuadCount = 0;
GrRect bounds;
GrIRect ibounds;
for (;;) {
GrPoint pts[4];
GrPathCmd cmd = (GrPathCmd)iter.next(pts);
switch (cmd) {
case kMove_PathCmd:
break;
case kLine_PathCmd:
m.mapPoints(pts,2);
bounds.setBounds(pts, 2);
bounds.outset(SK_Scalar1, SK_Scalar1);
bounds.roundOut(&ibounds);
if (SkIRect::Intersects(clip, ibounds)) {
lines->push_back() = pts[0];
lines->push_back() = pts[1];
}
break;
case kQuadratic_PathCmd: {
bounds.setBounds(pts, 3);
bounds.outset(SK_Scalar1, SK_Scalar1);
bounds.roundOut(&ibounds);
if (SkIRect::Intersects(clip, ibounds)) {
m.mapPoints(pts, 3);
int subdiv = num_quad_subdivs(pts);
GrAssert(subdiv >= -1);
if (-1 == subdiv) {
lines->push_back() = pts[0];
lines->push_back() = pts[1];
lines->push_back() = pts[1];
lines->push_back() = pts[2];
} else {
quads->push_back() = pts[0];
quads->push_back() = pts[1];
quads->push_back() = pts[2];
quadSubdivCnts->push_back() = subdiv;
totalQuadCount += 1 << subdiv;
}
}
} break;
case kCubic_PathCmd: {
bounds.setBounds(pts, 4);
bounds.outset(SK_Scalar1, SK_Scalar1);
bounds.roundOut(&ibounds);
if (SkIRect::Intersects(clip, ibounds)) {
m.mapPoints(pts, 4);
SkPoint stackStorage[32];
PtArray q((void*)stackStorage, 32);
convert_cubic_to_quads(pts, &q);
for (int i = 0; i < q.count(); i += 3) {
bounds.setBounds(&q[i], 3);
bounds.outset(SK_Scalar1, SK_Scalar1);
bounds.roundOut(&ibounds);
if (SkIRect::Intersects(clip, ibounds)) {
int subdiv = num_quad_subdivs(&q[i]);
GrAssert(subdiv >= -1);
if (-1 == subdiv) {
lines->push_back() = q[0 + i];
lines->push_back() = q[1 + i];
lines->push_back() = q[1 + i];
lines->push_back() = q[2 + i];
} else {
quads->push_back() = q[0 + i];
quads->push_back() = q[1 + i];
quads->push_back() = q[2 + i];
quadSubdivCnts->push_back() = subdiv;
totalQuadCount += 1 << subdiv;
}
}
}
}
} break;
case kClose_PathCmd:
break;
case kEnd_PathCmd:
return totalQuadCount;
}
}
}
struct Vertex {
GrPoint fPos;
union {
struct {
GrScalar fA;
GrScalar fB;
GrScalar fC;
} fLine;
GrVec fQuadCoord;
struct {
GrScalar fBogus[4];
};
};
};
GR_STATIC_ASSERT(sizeof(Vertex) == 3 * sizeof(GrPoint));
void intersect_lines(const SkPoint& ptA, const SkVector& normA,
const SkPoint& ptB, const SkVector& normB,
SkPoint* result) {
SkScalar lineAW = -normA.dot(ptA);
SkScalar lineBW = -normB.dot(ptB);
SkScalar wInv = SkScalarMul(normA.fX, normB.fY) -
SkScalarMul(normA.fY, normB.fX);
wInv = SkScalarInvert(wInv);
result->fX = SkScalarMul(normA.fY, lineBW) - SkScalarMul(lineAW, normB.fY);
result->fX = SkScalarMul(result->fX, wInv);
result->fY = SkScalarMul(lineAW, normB.fX) - SkScalarMul(normA.fX, lineBW);
result->fY = SkScalarMul(result->fY, wInv);
}
void bloat_quad(const SkPoint qpts[3], Vertex verts[kVertsPerQuad]) {
// original quad is specified by tri a,b,c
const SkPoint& a = qpts[0];
const SkPoint& b = qpts[1];
const SkPoint& c = qpts[2];
// make a new poly where we replace a and c by a 1-pixel wide edges orthog
// to edges ab and bc:
//
// before | after
// | b0
// b |
// |
// | a0 c0
// a c | a1 c1
//
// edges a0->b0 and b0->c0 are parallel to original edges a->b and b->c,
// respectively.
Vertex& a0 = verts[0];
Vertex& a1 = verts[1];
Vertex& b0 = verts[2];
Vertex& c0 = verts[3];
Vertex& c1 = verts[4];
// compute a matrix that goes from device coords to U,V quad params
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);
SkVector ab = b;
ab -= a;
SkVector ac = c;
ac -= a;
SkVector cb = b;
cb -= c;
// We should have already handled degenerates
GrAssert(ab.length() > 0 && cb.length() > 0);
ab.normalize();
SkVector abN;
abN.setOrthog(ab, SkVector::kLeft_Side);
if (abN.dot(ac) > 0) {
abN.negate();
}
cb.normalize();
SkVector cbN;
cbN.setOrthog(cb, SkVector::kLeft_Side);
if (cbN.dot(ac) < 0) {
cbN.negate();
}
a0.fPos = a;
a0.fPos += abN;
a1.fPos = a;
a1.fPos -= abN;
c0.fPos = c;
c0.fPos += cbN;
c1.fPos = c;
c1.fPos -= cbN;
intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos);
DevToUV.mapPointsWithStride(&verts[0].fQuadCoord,
&verts[0].fPos, sizeof(Vertex), kVertsPerQuad);
}
void add_quads(const SkPoint p[3],
int subdiv,
Vertex** vert) {
GrAssert(subdiv >= 0);
if (subdiv) {
SkPoint newP[5];
SkChopQuadAtHalf(p, newP);
add_quads(newP + 0, subdiv-1, vert);
add_quads(newP + 2, subdiv-1, vert);
} else {
bloat_quad(p, *vert);
*vert += kVertsPerQuad;
}
}
void add_line(const SkPoint p[2],
int rtHeight,
Vertex** vert) {
const SkPoint& a = p[0];
const SkPoint& b = p[1];
SkVector orthVec = b;
orthVec -= a;
if (orthVec.setLength(SK_Scalar1)) {
orthVec.setOrthog(orthVec);
// the values we pass down to the frag shader
// have to be in y-points-up space;
SkVector normal;
normal.fX = orthVec.fX;
normal.fY = -orthVec.fY;
SkPoint aYDown;
aYDown.fX = a.fX;
aYDown.fY = rtHeight - a.fY;
SkScalar lineC = -(aYDown.dot(normal));
for (int i = 0; i < kVertsPerLineSeg; ++i) {
(*vert)[i].fPos = (i < 2) ? a : b;
if (0 == i || 3 == i) {
(*vert)[i].fPos -= orthVec;
} else {
(*vert)[i].fPos += orthVec;
}
(*vert)[i].fLine.fA = normal.fX;
(*vert)[i].fLine.fB = normal.fY;
(*vert)[i].fLine.fC = lineC;
}
} else {
// just make it degenerate and likely offscreen
(*vert)[0].fPos.set(SK_ScalarMax, SK_ScalarMax);
(*vert)[1].fPos.set(SK_ScalarMax, SK_ScalarMax);
(*vert)[2].fPos.set(SK_ScalarMax, SK_ScalarMax);
(*vert)[3].fPos.set(SK_ScalarMax, SK_ScalarMax);
}
*vert += kVertsPerLineSeg;
}
}
bool GrAAHairLinePathRenderer::createGeom(GrDrawTarget::StageBitfield stages) {
int rtHeight = fTarget->getRenderTarget()->height();
GrIRect clip;
if (fTarget->getClip().hasConservativeBounds()) {
GrRect clipRect = fTarget->getClip().getConservativeBounds();
clipRect.roundOut(&clip);
} else {
clip.setLargest();
}
if (stages == fPreviousStages &&
fPreviousViewMatrix == fTarget->getViewMatrix() &&
rtHeight == fPreviousRTHeight &&
fClipRect == clip) {
return true;
}
GrVertexLayout layout = GrDrawTarget::kEdge_VertexLayoutBit;
for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
if ((1 << s) & stages) {
layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
}
}
GrMatrix viewM = fTarget->getViewMatrix();
viewM.preTranslate(fTranslate.fX, fTranslate.fY);
GrAlignedSTStorage<128, GrPoint> lineStorage;
GrAlignedSTStorage<128, GrPoint> quadStorage;
PtArray lines(&lineStorage);
PtArray quads(&quadStorage);
IntArray qSubdivs;
fQuadCnt = get_lines_and_quads(*fPath, viewM, clip,
&lines, &quads, &qSubdivs);
fLineSegmentCnt = lines.count() / 2;
int vertCnt = kVertsPerLineSeg * fLineSegmentCnt + kVertsPerQuad * fQuadCnt;
GrAssert(sizeof(Vertex) == GrDrawTarget::VertexSize(layout));
Vertex* verts;
if (!fTarget->reserveVertexSpace(layout, vertCnt, (void**)&verts)) {
return false;
}
for (int i = 0; i < fLineSegmentCnt; ++i) {
add_line(&lines[2*i], rtHeight, &verts);
}
int unsubdivQuadCnt = quads.count() / 3;
for (int i = 0; i < unsubdivQuadCnt; ++i) {
GrAssert(qSubdivs[i] >= 0);
add_quads(&quads[3*i], qSubdivs[i], &verts);
}
fPreviousStages = stages;
fPreviousViewMatrix = fTarget->getViewMatrix();
fPreviousRTHeight = rtHeight;
fClipRect = clip;
return true;
}
void GrAAHairLinePathRenderer::drawPath(GrDrawTarget::StageBitfield stages) {
GrDrawTarget::AutoStateRestore asr(fTarget);
GrMatrix ivm;
if (!this->createGeom(stages)) {
return;
}
if (fTarget->getViewInverse(&ivm)) {
fTarget->preConcatSamplerMatrices(stages, ivm);
}
fTarget->setViewMatrix(GrMatrix::I());
// TODO: See whether rendering lines as degenerate quads improves perf
// when we have a mix
fTarget->setIndexSourceToBuffer(fLinesIndexBuffer);
int lines = 0;
int nBufLines = fLinesIndexBuffer->maxQuads();
while (lines < fLineSegmentCnt) {
int n = GrMin(fLineSegmentCnt-lines, nBufLines);
fTarget->setVertexEdgeType(GrDrawTarget::kHairLine_EdgeType);
fTarget->drawIndexed(kTriangles_PrimitiveType,
kVertsPerLineSeg*lines, // startV
0, // startI
kVertsPerLineSeg*n, // vCount
kIdxsPerLineSeg*n); // iCount
lines += n;
}
fTarget->setIndexSourceToBuffer(fQuadsIndexBuffer);
int quads = 0;
while (quads < fQuadCnt) {
int n = GrMin(fQuadCnt-quads, kNumQuadsInIdxBuffer);
fTarget->setVertexEdgeType(GrDrawTarget::kHairQuad_EdgeType);
fTarget->drawIndexed(kTriangles_PrimitiveType,
4*fLineSegmentCnt + kVertsPerQuad*quads, // startV
0, // startI
kVertsPerQuad*n, // vCount
kIdxsPerQuad*n); // iCount
quads += n;
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2011 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrAAHairLinePathRenderer_DEFINED
#define GrAAHairLinePathRenderer_DEFINED
#include "GrPathRenderer.h"
class GrAAHairLinePathRenderer : public GrPathRenderer {
public:
virtual ~GrAAHairLinePathRenderer();
static GrPathRenderer* Create(GrContext* context);
// GrPathRenderer overrides
virtual bool supportsAA(GrDrawTarget* target,
const SkPath& path,
GrPathFill fill);
virtual bool canDrawPath(const GrDrawTarget* target,
const SkPath& path,
GrPathFill fill) const;
virtual void drawPath(GrDrawTarget::StageBitfield stages);
protected:
// GrPathRenderer overrides
virtual void pathWillClear();
private:
void resetGeom();
static bool CanBeUsed(const GrContext* context);
GrAAHairLinePathRenderer(const GrContext* context,
const GrIndexBuffer* fLinesIndexBuffer,
const GrIndexBuffer* fQuadsIndexBuffer);
bool createGeom(GrDrawTarget::StageBitfield stages);
GrContext* fContext;
const GrIndexBuffer* fLinesIndexBuffer;
const GrIndexBuffer* fQuadsIndexBuffer;
// have to recreate geometry if stages in use changes :(
GrDrawTarget::StageBitfield fPreviousStages;
int fPreviousRTHeight;
GrIRect fClipRect;
// this path renderer draws everything in device coordinates
GrMatrix fPreviousViewMatrix;
int fLineSegmentCnt;
int fQuadCnt;
typedef GrPathRenderer INHERITED;
};
#endif

View File

@ -0,0 +1,20 @@
/*
* 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 "GrAAHairLinePathRenderer.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();
}
}
}

View File

@ -1176,7 +1176,7 @@ void GrContext::drawRect(const GrPaint& paint,
GrRect devRect = rect;
GrMatrix combinedMatrix;
bool doAA = apply_aa_to_rect(target, paint, rect, width, matrix,
bool doAA = apply_aa_to_rect(target, paint, rect, width, matrix,
&combinedMatrix, &devRect);
if (doAA) {
@ -1378,9 +1378,12 @@ void GrContext::drawVertices(const GrPaint& paint,
}
int texOffsets[GrDrawTarget::kMaxTexCoords];
int colorOffset;
int edgeOffset;
GrDrawTarget::VertexSizeAndOffsetsByIdx(layout,
texOffsets,
&colorOffset);
&colorOffset,
&edgeOffset);
GrAssert(-1 == edgeOffset);
void* curVertex = geo.vertices();
for (int i = 0; i < vertexCount; ++i) {

View File

@ -20,7 +20,8 @@ public:
GrDefaultPathRenderer(bool separateStencilSupport,
bool stencilWrapOpsSupport);
virtual bool canDrawPath(const SkPath& path,
virtual bool canDrawPath(const GrDrawTarget* target,
const SkPath& path,
GrPathFill fill) const { return true; }
virtual bool requiresStencilPass(const GrDrawTarget* target,

View File

@ -63,6 +63,17 @@ bool check_layout(GrVertexLayout layout) {
return true;
}
int num_tex_coords(GrVertexLayout layout) {
int cnt = 0;
// figure out how many tex coordinates are present
for (int t = 0; t < GrDrawTarget::kMaxTexCoords; ++t) {
if (tex_coord_idx_mask(t) & layout) {
++cnt;
}
}
return cnt;
}
} //unnamed namespace
size_t GrDrawTarget::VertexSize(GrVertexLayout vertexLayout) {
@ -73,14 +84,13 @@ size_t GrDrawTarget::VertexSize(GrVertexLayout vertexLayout) {
sizeof(GrPoint);
size_t size = vecSize; // position
for (int t = 0; t < kMaxTexCoords; ++t) {
if (tex_coord_idx_mask(t) & vertexLayout) {
size += vecSize;
}
}
size += num_tex_coords(vertexLayout) * vecSize;
if (vertexLayout & kColor_VertexLayoutBit) {
size += sizeof(GrColor);
}
if (vertexLayout & kEdge_VertexLayoutBit) {
size += 4 * sizeof(GrScalar);
}
return size;
}
@ -111,16 +121,27 @@ int GrDrawTarget::VertexStageCoordOffset(int stage, GrVertexLayout vertexLayout)
int GrDrawTarget::VertexColorOffset(GrVertexLayout vertexLayout) {
GrAssert(check_layout(vertexLayout));
// color is after the pos and tex coords
if (vertexLayout & kColor_VertexLayoutBit) {
int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
sizeof(GrGpuTextVertex) :
sizeof(GrPoint);
int offset = vecSize; // position
// figure out how many tex coordinates are present and precede this one.
for (int t = 0; t < kMaxTexCoords; ++t) {
if (tex_coord_idx_mask(t) & vertexLayout) {
offset += vecSize;
}
return vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
}
return -1;
}
int GrDrawTarget::VertexEdgeOffset(GrVertexLayout vertexLayout) {
GrAssert(check_layout(vertexLayout));
// edge pts are after the pos, tex coords, and color
if (vertexLayout & kEdge_VertexLayoutBit) {
int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
sizeof(GrGpuTextVertex) :
sizeof(GrPoint);
int offset = vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
if (vertexLayout & kColor_VertexLayoutBit) {
offset += sizeof(GrColor);
}
return offset;
}
@ -129,11 +150,13 @@ int GrDrawTarget::VertexColorOffset(GrVertexLayout vertexLayout) {
int GrDrawTarget::VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout,
int texCoordOffsetsByIdx[kMaxTexCoords],
int* colorOffset) {
int* colorOffset,
int* edgeOffset) {
GrAssert(check_layout(vertexLayout));
GrAssert(NULL != texCoordOffsetsByIdx);
GrAssert(NULL != colorOffset);
GrAssert(NULL != edgeOffset);
int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
sizeof(GrGpuTextVertex) :
@ -154,21 +177,30 @@ int GrDrawTarget::VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout,
} else {
*colorOffset = -1;
}
if (kEdge_VertexLayoutBit & vertexLayout) {
*edgeOffset = size;
size += 4 * sizeof(GrScalar);
} else {
*edgeOffset = -1;
}
return size;
}
int GrDrawTarget::VertexSizeAndOffsetsByStage(GrVertexLayout vertexLayout,
int texCoordOffsetsByStage[kNumStages],
int* colorOffset) {
int* colorOffset,
int* edgeOffset) {
GrAssert(check_layout(vertexLayout));
GrAssert(NULL != texCoordOffsetsByStage);
GrAssert(NULL != colorOffset);
GrAssert(NULL != edgeOffset);
int texCoordOffsetsByIdx[kMaxTexCoords];
int size = VertexSizeAndOffsetsByIdx(vertexLayout,
texCoordOffsetsByIdx,
colorOffset);
colorOffset,
edgeOffset);
for (int s = 0; s < kNumStages; ++s) {
int tcIdx;
if (StagePosAsTexCoordVertexLayoutBit(s) & vertexLayout) {
@ -250,22 +282,39 @@ void GrDrawTarget::VertexLayoutUnitTest() {
GrAssert(VertexUsesStage(s2, posAsTex));
GrAssert(2*sizeof(GrPoint) == VertexSize(posAsTex));
GrAssert(-1 == VertexTexCoordsForStage(s2, posAsTex));
GrAssert(-1 == VertexEdgeOffset(posAsTex));
}
GrAssert(-1 == VertexEdgeOffset(tcMask));
GrAssert(-1 == VertexColorOffset(tcMask));
#if GR_DEBUG
GrVertexLayout withColor = tcMask | kColor_VertexLayoutBit;
#endif
GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColor));
GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColor));
#if GR_DEBUG
GrVertexLayout withEdge = tcMask | kEdge_VertexLayoutBit;
#endif
GrAssert(-1 == VertexColorOffset(withEdge));
GrAssert(2*sizeof(GrPoint) == VertexEdgeOffset(withEdge));
GrAssert(4*sizeof(GrPoint) == VertexSize(withEdge));
#if GR_DEBUG
GrVertexLayout withColorAndEdge = withColor | kEdge_VertexLayoutBit;
#endif
GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColorAndEdge));
GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexEdgeOffset(withColorAndEdge));
GrAssert(4*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColorAndEdge));
}
GrAssert(tex_coord_idx_mask(t) == tcMask);
GrAssert(check_layout(tcMask));
int stageOffsets[kNumStages];
int colorOffset;
int edgeOffset;
int size;
size = VertexSizeAndOffsetsByStage(tcMask, stageOffsets, &colorOffset);
size = VertexSizeAndOffsetsByStage(tcMask, stageOffsets, &colorOffset, &edgeOffset);
GrAssert(2*sizeof(GrPoint) == size);
GrAssert(-1 == colorOffset);
GrAssert(-1 == edgeOffset);
for (int s = 0; s < kNumStages; ++s) {
GrAssert(VertexUsesStage(s, tcMask));
GrAssert(sizeof(GrPoint) == stageOffsets[s]);
@ -698,7 +747,8 @@ void GrDrawTarget::drawNonIndexed(GrPrimitiveType type,
bool GrDrawTarget::CanDisableBlend(GrVertexLayout layout, const DrState& state) {
// If we compute a coverage value (using edge AA or a coverage stage) then
// we can't force blending off.
if (state.fEdgeAANumEdges > 0) {
if (state.fEdgeAANumEdges > 0 ||
layout & kEdge_VertexLayoutBit) {
return false;
}
for (int s = state.fFirstCoverageStage; s < kNumStages; ++s) {
@ -837,8 +887,11 @@ void GrDrawTarget::SetRectVertices(const GrRect& rect,
int stageOffsets[kNumStages];
int colorOffset;
int vsize = VertexSizeAndOffsetsByStage(layout, stageOffsets, &colorOffset);
int edgeOffset;
int vsize = VertexSizeAndOffsetsByStage(layout, stageOffsets,
&colorOffset, &edgeOffset);
GrAssert(-1 == colorOffset);
GrAssert(-1 == edgeOffset);
GrTCast<GrPoint*>(vertices)->setRectFan(rect.fLeft, rect.fTop,
rect.fRight, rect.fBottom,

View File

@ -59,6 +59,20 @@ public:
kMaxEdges = 32
};
/**
* When specifying edges as vertex data this enum specifies what type of
* edges are in use. The edges are always 4 GrScalars in memory, even when
* the edge type requires fewer than 4.
*/
enum VertexEdgeType {
/* 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
};
/**
* Bitfield used to indicate which stages are in use.
*/
@ -167,6 +181,7 @@ protected:
GrStencilSettings fStencilSettings;
GrMatrix fViewMatrix;
VertexEdgeType fVertexEdgeType;
Edge fEdgeAAEdges[kMaxEdges];
int fEdgeAANumEdges;
bool operator ==(const DrState& s) const {
@ -256,7 +271,7 @@ public:
}
/**
* Shortcut for preConcatSamplerMatrix on all stages in mask with same
* Shortcut for preConcatSamplerMatrix on all stages in mask with same
* matrix
*/
void preConcatSamplerMatrices(int stageMask, const GrMatrix& matrix) {
@ -267,6 +282,18 @@ public:
}
}
/**
* Shortcut for preConcatSamplerMatrix on all enabled stages in mask with
* same matrix
*
* @param stage the stage of the sampler to set
* @param matrix the matrix to concat
*/
void preConcatEnabledSamplerMatrices(const GrMatrix& matrix) {
StageBitfield stageMask = this->enabledStages();
this->preConcatSamplerMatrices(stageMask, matrix);
}
/**
* Gets the matrix of a stage's sampler
*
@ -535,6 +562,15 @@ public:
*/
bool canDisableBlend() const;
/**
* Determines the interpretation per-vertex edge data when the
* kEdge_VertexLayoutBit is set (see below). When per-vertex edges are not
* specified the value of this setting has no effect.
*/
void setVertexEdgeType(VertexEdgeType type) {
fCurrDrawState.fVertexEdgeType = type;
}
/**
* Given the current draw state, vertex layout, and hw support, will HW AA
* lines be used (if line primitive type is drawn)? (Note that lines are
@ -575,15 +611,14 @@ public:
* Additional Bits that can be specified in GrVertexLayout.
*/
enum VertexLayoutBits {
/* vertices have colors */
kColor_VertexLayoutBit = 1 << (STAGE_BIT_CNT + 0),
//<! vertices have colors
/* Use text vertices. (Pos and tex coords may be a different type for
text [GrGpuTextVertex vs GrPoint].) */
kTextFormat_VertexLayoutBit = 1 << (STAGE_BIT_CNT + 1),
//<! use text vertices. (Pos
// and tex coords may be
// a different type for
// text [GrGpuTextVertex vs
// GrPoint].)
kEdge_VertexLayoutBit = 1 << (STAGE_BIT_CNT + 2),
// for below assert
kDummyVertexLayoutBit,
kHighVertexLayoutBit = kDummyVertexLayoutBit - 1
@ -1030,6 +1065,13 @@ public:
*/
static int VertexColorOffset(GrVertexLayout vertexLayout);
/**
* Helper function to compute the offset of the edge pts in a vertex
* @return offset of edge in vertex layout or -1 if the
* layout has no edge.
*/
static int VertexEdgeOffset(GrVertexLayout vertexLayout);
/**
* Helper function to determine if vertex layout contains explicit texture
* coordinates of some index.
@ -1069,7 +1111,8 @@ public:
*/
static int VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout,
int texCoordOffsetsByIdx[kMaxTexCoords],
int *colorOffset);
int *colorOffset,
int* edgeOffset);
/**
* Helper function to compute the size of each vertex and the offsets of
@ -1086,7 +1129,8 @@ public:
*/
static int VertexSizeAndOffsetsByStage(GrVertexLayout vertexLayout,
int texCoordOffsetsByStage[kNumStages],
int *colorOffset);
int *colorOffset,
int* edgeOffset);
/**
* Accessing positions, texture coords, or colors, of a vertex within an
@ -1185,7 +1229,7 @@ protected:
// given a vertex layout and a draw state, will a stage be used?
static bool StageWillBeUsed(int stage, GrVertexLayout layout,
const DrState& state) {
const DrState& state) {
return NULL != state.fTextures[stage] && VertexUsesStage(stage, layout);
}
@ -1194,6 +1238,14 @@ protected:
fCurrDrawState);
}
StageBitfield enabledStages() const {
StageBitfield mask = 0;
for (int s = 0; s < kNumStages; ++s) {
mask |= this->isStageEnabled(s) ? 1 : 0;
}
return mask;
}
// Helpers for GrDrawTarget subclasses that won't have private access to
// SavedDrawState but need to peek at the state values.
static DrState& accessSavedDrawState(SavedDrawState& sds)

View File

@ -44,6 +44,7 @@ const char* GrShaderPrecision(const GrGLInterface* gl) {
#define POS_ATTR_NAME "aPosition"
#define COL_ATTR_NAME "aColor"
#define EDGE_ATTR_NAME "aEdge"
#define COL_UNI_NAME "uColor"
#define EDGES_UNI_NAME "uEdges"
#define COL_FILTER_UNI_NAME "uColorFilter"
@ -539,6 +540,25 @@ bool GrGLProgram::genProgram(const GrGLInterface* gl,
segments.fFSCode.append(";\n");
}
inCoverage = "edgeAlpha";
} else if (layout & GrDrawTarget::kEdge_VertexLayoutBit) {
segments.fVSAttrs.append("attribute vec4 " EDGE_ATTR_NAME ";\n");
segments.fVaryings.append("varying vec4 vEdge;\n");
segments.fVSCode.append("\tvEdge = " EDGE_ATTR_NAME ";\n");
if (GrDrawTarget::kHairLine_EdgeType == fProgramDesc.fVertexEdgeType) {
segments.fFSCode.append("\tfloat edgeAlpha = abs(dot(vec3(gl_FragCoord.xy,1), vEdge.xyz));\n");
} else {
GrAssert(GrDrawTarget::kHairQuad_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.append("\tvec2 duvdx = dFdx(vEdge.xy);\n");
segments.fFSCode.append("\tvec2 duvdy = dFdy(vEdge.xy);\n");
segments.fFSCode.append("\tfloat dfdx = 2.0*vEdge.x*duvdx.x - duvdx.y;\n");
segments.fFSCode.append("\tfloat dfdy = 2.0*vEdge.x*duvdy.x - duvdy.y;\n");
segments.fFSCode.append("\tfloat edgeAlpha = (vEdge.x*vEdge.x - vEdge.y);\n");
segments.fFSCode.append("\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / (dfdx*dfdx + dfdy*dfdy));\n");
}
segments.fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
inCoverage = "edgeAlpha";
}
GrStringBuilder outCoverage;
@ -839,8 +859,10 @@ bool GrGLProgram::bindOutputsAttribsAndLinkProgram(
}
}
GR_GL_CALL(gl, BindAttribLocation(progID, ColorAttributeIdx(),
GR_GL_CALL(gl, BindAttribLocation(progID, ColorAttributeIdx(),
COL_ATTR_NAME));
GR_GL_CALL(gl, BindAttribLocation(progID, EdgeAttributeIdx(),
EDGE_ATTR_NAME));
GR_GL_CALL(gl, LinkProgram(progID));

View File

@ -65,6 +65,8 @@ public:
static int PositionAttributeIdx() { return 0; }
static int TexCoordAttributeIdx(int tcIdx) { return 1 + tcIdx; }
static int ColorAttributeIdx() { return 1 + GrDrawTarget::kMaxTexCoords; }
static int EdgeAttributeIdx() { return 2 + GrDrawTarget::kMaxTexCoords; }
static int ViewMatrixAttributeIdx() {
return 2 + GrDrawTarget::kMaxTexCoords;
}
@ -150,6 +152,8 @@ private:
kDualSrcOutputCnt
};
GrDrawTarget::VertexEdgeType fVertexEdgeType;
// stripped of bits that don't affect prog generation
GrVertexLayout fVertexLayout;

View File

@ -276,15 +276,22 @@ void GrGpuGLFixed::setupGeometry(int* startVertex,
int newColorOffset;
int newTexCoordOffsets[kNumStages];
int newEdgeOffset;
GrGLsizei newStride = VertexSizeAndOffsetsByStage(this->getGeomSrc().fVertexLayout,
newTexCoordOffsets,
&newColorOffset);
&newColorOffset,
&newEdgeOffset);
GrAssert(-1 == newEdgeOffset); // not supported by fixed pipe
int oldColorOffset;
int oldTexCoordOffsets[kNumStages];
int oldEdgeOffset;
GrGLsizei oldStride = VertexSizeAndOffsetsByStage(fHWGeometryState.fVertexLayout,
oldTexCoordOffsets,
&oldColorOffset);
&oldColorOffset,
&oldEdgeOffset);
GrAssert(-1 == oldEdgeOffset);
bool indexed = NULL != startIndex;

View File

@ -185,11 +185,24 @@ bool GrGpuGLShaders::programUnitTest() {
pdesc.fFirstCoverageStage = idx;
bool edgeAA = random.nextF() > .5f;
if (edgeAA) {
pdesc.fEdgeAANumEdges = random.nextF() * this->getMaxEdges() + 1;
pdesc.fEdgeAAConcave = random.nextF() > .5f;
} else {
pdesc.fEdgeAANumEdges = 0;
if (edgeAA) {
bool vertexEdgeAA = random.nextF() > .5f;
if (vertexEdgeAA) {
pdesc.fVertexLayout |= GrDrawTarget::kEdge_VertexLayoutBit;
if (this->supportsShaderDerivatives()) {
pdesc.fVertexEdgeType = random.nextF() > 0.5f ?
kHairQuad_EdgeType :
kHairLine_EdgeType;
} else {
pdesc.fVertexEdgeType = kHairLine_EdgeType;
}
pdesc.fEdgeAANumEdges = 0;
} else {
pdesc.fEdgeAANumEdges = random.nextF() * this->getMaxEdges() + 1;
pdesc.fEdgeAAConcave = random.nextF() > .5f;
}
} else {
pdesc.fEdgeAANumEdges = 0;
}
if (fDualSourceBlendingSupport) {
@ -307,6 +320,7 @@ void GrGpuGLShaders::resetContext() {
fHWGeometryState.fVertexLayout = 0;
fHWGeometryState.fVertexOffset = ~0;
GL_CALL(DisableVertexAttribArray(GrGLProgram::ColorAttributeIdx()));
GL_CALL(DisableVertexAttribArray(GrGLProgram::EdgeAttributeIdx()));
for (int t = 0; t < kMaxTexCoords; ++t) {
GL_CALL(DisableVertexAttribArray(GrGLProgram::TexCoordAttributeIdx(t)));
}
@ -641,17 +655,22 @@ void GrGpuGLShaders::setupGeometry(int* startVertex,
int newColorOffset;
int newTexCoordOffsets[kMaxTexCoords];
int newEdgeOffset;
GrGLsizei newStride = VertexSizeAndOffsetsByIdx(
this->getGeomSrc().fVertexLayout,
newTexCoordOffsets,
&newColorOffset);
&newColorOffset,
&newEdgeOffset);
int oldColorOffset;
int oldTexCoordOffsets[kMaxTexCoords];
int oldEdgeOffset;
GrGLsizei oldStride = VertexSizeAndOffsetsByIdx(
fHWGeometryState.fVertexLayout,
oldTexCoordOffsets,
&oldColorOffset);
&oldColorOffset,
&oldEdgeOffset);
bool indexed = NULL != startIndex;
int extraVertexOffset;
@ -727,6 +746,21 @@ void GrGpuGLShaders::setupGeometry(int* startVertex,
GL_CALL(DisableVertexAttribArray(GrGLProgram::ColorAttributeIdx()));
}
if (newEdgeOffset > 0) {
GrGLvoid* edgeOffset = (int8_t*)(vertexOffset + newEdgeOffset);
int idx = GrGLProgram::EdgeAttributeIdx();
if (oldEdgeOffset <= 0) {
GL_CALL(EnableVertexAttribArray(idx));
GL_CALL(VertexAttribPointer(idx, 4, scalarType,
false, newStride, edgeOffset));
} else if (allOffsetsChange || newEdgeOffset != oldEdgeOffset) {
GL_CALL(VertexAttribPointer(idx, 4, scalarType,
false, newStride, edgeOffset));
}
} else if (oldEdgeOffset > 0) {
GL_CALL(DisableVertexAttribArray(GrGLProgram::EdgeAttributeIdx()));
}
fHWGeometryState.fVertexLayout = this->getGeomSrc().fVertexLayout;
fHWGeometryState.fArrayPtrsDirty = false;
}
@ -734,6 +768,11 @@ void GrGpuGLShaders::setupGeometry(int* startVertex,
void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
ProgramDesc& desc = fCurrentProgram.fProgramDesc;
// The descriptor is used as a cache key. Thus when a field of the
// descriptor will not affect program generation (because of the vertex
// layout in use or other descriptor field settings) it should be set
// to a canonical value to avoid duplicate programs with different keys.
// Must initialize all fields or cache will have false negatives!
desc.fVertexLayout = this->getGeomSrc().fVertexLayout;
@ -767,6 +806,13 @@ void GrGpuGLShaders::buildProgram(GrPrimitiveType type) {
int lastEnabledStage = -1;
if (desc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit) {
desc.fVertexEdgeType = fCurrDrawState.fVertexEdgeType;
} else {
// use canonical value when not set to avoid cache misses
desc.fVertexEdgeType = GrDrawTarget::kHairLine_EdgeType;
}
for (int s = 0; s < kNumStages; ++s) {
StageDesc& stage = desc.fStages[s];

View File

@ -52,13 +52,28 @@ public:
/**
* Returns true if this path renderer is able to render the path.
* Returning false allows the caller to fallback to another path renderer.
* When searching for a path renderer capable of rendering a path this
* function is called. The path renderer can examine the path, fill rule,
* and draw settings that will be used (via the targetparameter). If "true"
* is reported note that the caller is permitted to make modifications to
* the following settings of the target between the calls to canDrawPath and
* drawPath:
* 1. view matrix: The matrix at drawPath time may have additional scale
* scale and translation applied
* 2. render target: The render target may change between canDrawPath
* and drawPath.
* The GrPathRenderer subclass's decision about whether to return true
* or false in its implementation of this function should consider these
* possible state changes.
*
* @param path The path to draw
* @param fill The fill rule to use
*
* @return true if the path can be drawn by this object, false otherwise.
*/
virtual bool canDrawPath(const SkPath& path, GrPathFill fill) const = 0;
virtual bool canDrawPath(const GrDrawTarget* target,
const SkPath& path,
GrPathFill fill) const = 0;
/**
* For complex clips Gr uses the stencil buffer. The path renderer must be

View File

@ -43,7 +43,7 @@ GrPathRenderer* GrPathRendererChain::getPathRenderer(const GrDrawTarget* target,
!target->getRenderTarget()->isMultisampled();
GrPathRenderer* nonAAPR = NULL;
for (int i = 0; i < fChain.count(); ++i) {
if (fChain[i]->canDrawPath(path, fill)) {
if (fChain[i]->canDrawPath(target, path, fill)) {
if (!preferAA || fChain[i]->supportsAA(target, path, fill)) {
return fChain[i];
} else {

View File

@ -599,7 +599,8 @@ FINISHED:
}
}
bool GrTesselatedPathRenderer::canDrawPath(const SkPath& path,
bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
const SkPath& path,
GrPathFill fill) const {
return kHairLine_PathFill != fill;
}

View File

@ -17,7 +17,8 @@ public:
GrTesselatedPathRenderer();
virtual void drawPath(GrDrawTarget::StageBitfield stages);
virtual bool canDrawPath(const GrPath& path,
virtual bool canDrawPath(const GrDrawTarget* target,
const GrPath& path,
GrPathFill fill) const;
virtual bool requiresStencilPass(const GrDrawTarget* target,

View File

@ -34,6 +34,7 @@
'../samplecode/ClockFaceView.cpp',
'../samplecode/OverView.cpp',
'../samplecode/Sample2PtRadial.cpp',
'../samplecode/SampleAARects.cpp',
'../samplecode/SampleAll.cpp',
'../samplecode/SampleAnimator.cpp',
'../samplecode/SampleApp.cpp',
@ -68,6 +69,7 @@
'../samplecode/SampleFuzz.cpp',
'../samplecode/SampleGM.cpp',
'../samplecode/SampleGradients.cpp',
'../samplecode/SampleHairCurves.cpp',
'../samplecode/SampleHairline.cpp',
'../samplecode/SampleImage.cpp',
'../samplecode/SampleImageDir.cpp',
@ -90,7 +92,6 @@
'../samplecode/SamplePicture.cpp',
'../samplecode/SamplePoints.cpp',
'../samplecode/SamplePolyToPoly.cpp',
'../samplecode/SampleAARects.cpp',
'../samplecode/SampleRegion.cpp',
'../samplecode/SampleRepeatTile.cpp',
'../samplecode/SampleShaders.cpp',

View File

@ -126,7 +126,9 @@
'../gpu/include/GrTypes.h',
'../gpu/include/GrUserConfig.h',
'../gpu/src/GrAddPathRenderers_none.cpp',
'../gpu/src/GrAAHairlinePathRenderer.cpp',
'../gpu/src/GrAAHairlinePathRenderer.h',
'../gpu/src/GrAddPathRenderers_aahairline.cpp',
'../gpu/src/GrAllocPool.cpp',
'../gpu/src/GrAtlas.cpp',
'../gpu/src/GrBinHashKey.h',

View File

@ -0,0 +1,112 @@
/*
* 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 "SampleCode.h"
#include "SkView.h"
#include "SkCanvas.h"
#include "SkPath.h"
#include "SkRandom.h"
class HairCurvesView : public SampleView {
public:
HairCurvesView() {
}
protected:
// overrides from SkEventSink
virtual bool onQuery(SkEvent* evt) {
if (SampleCode::TitleQ(*evt)) {
SampleCode::TitleR(evt, "HairCurves");
return true;
}
return this->INHERITED::onQuery(evt);
}
virtual void onDrawContent(SkCanvas* canvas) {
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(-1);
canvas->save();
canvas->scale(1000 * SK_Scalar1, 1000 * SK_Scalar1);
SkRandom rand;
SkPath curves;
SkPath hulls;
SkPath ctrlPts;
for (int i = 0; i < 100; ++i) {
SkScalar pts[] = {
rand.nextUScalar1(), rand.nextUScalar1(),
rand.nextUScalar1(), rand.nextUScalar1(),
rand.nextUScalar1(), rand.nextUScalar1(),
rand.nextUScalar1(), rand.nextUScalar1()
};
curves.moveTo(pts[0], pts[1]);
curves.cubicTo(pts[2], pts[3],
pts[4], pts[5],
pts[6], pts[7]);
hulls.moveTo(pts[0], pts[1]);
hulls.lineTo(pts[2], pts[3]);
hulls.lineTo(pts[4], pts[5]);
hulls.lineTo(pts[6], pts[7]);
ctrlPts.addCircle(pts[0], pts[1], SK_Scalar1 / 200);
ctrlPts.addCircle(pts[2], pts[3], SK_Scalar1 / 200);
ctrlPts.addCircle(pts[4], pts[5], SK_Scalar1 / 200);
ctrlPts.addCircle(pts[6], pts[7], SK_Scalar1 / 200);
}
for (int i = 0; i < 100; ++i) {
SkScalar pts[] = {
rand.nextUScalar1(), rand.nextUScalar1(),
rand.nextUScalar1(), rand.nextUScalar1(),
rand.nextUScalar1(), rand.nextUScalar1(),
};
curves.moveTo(pts[0], pts[1]);
curves.quadTo(pts[2], pts[3],
pts[4], pts[5]);
hulls.moveTo(pts[0], pts[1]);
hulls.lineTo(pts[2], pts[3]);
hulls.lineTo(pts[4], pts[5]);
ctrlPts.addCircle(pts[0], pts[1], SK_Scalar1 / 200);
ctrlPts.addCircle(pts[2], pts[3], SK_Scalar1 / 200);
ctrlPts.addCircle(pts[4], pts[5], SK_Scalar1 / 200);
}
for (int i = 0; i < 100; ++i) {
SkScalar pts[] = {
rand.nextUScalar1(), rand.nextUScalar1(),
rand.nextUScalar1(), rand.nextUScalar1(),
};
curves.moveTo(pts[0], pts[1]);
curves.lineTo(pts[2], pts[3]);
ctrlPts.addCircle(pts[0], pts[1], SK_Scalar1 / 200);
ctrlPts.addCircle(pts[2], pts[3], SK_Scalar1 / 200);
}
paint.setColor(SK_ColorBLACK);
canvas->drawPath(curves, paint);
paint.setColor(SK_ColorRED);
//canvas->drawPath(hulls, paint);
paint.setStyle(SkPaint::kFill_Style);
paint.setColor(SK_ColorBLUE);
//canvas->drawPath(ctrlPts, paint);
canvas->restore();
}
private:
typedef SampleView INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
static SkView* MyFactory() { return new HairCurvesView; }
static SkViewRegister reg(MyFactory);