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:
parent
dec9f2d351
commit
aeb2160b1d
633
gpu/src/GrAAHairLinePathRenderer.cpp
Normal file
633
gpu/src/GrAAHairLinePathRenderer.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
62
gpu/src/GrAAHairLinePathRenderer.h
Normal file
62
gpu/src/GrAAHairLinePathRenderer.h
Normal 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
|
||||
|
20
gpu/src/GrAddPathRenderers_aahairline.cpp
Normal file
20
gpu/src/GrAddPathRenderers_aahairline.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
112
samplecode/SampleHairCurves.cpp
Normal file
112
samplecode/SampleHairCurves.cpp
Normal 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);
|
||||
|
Loading…
Reference in New Issue
Block a user