| Index: src/gpu/GrAAHairLinePathRenderer.cpp
|
| diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp
|
| index c1f951ab7f91832d95ece209f71b377794c68e48..47669b4d9b8b37317f44a7e4cf551444bf9d3015 100644
|
| --- a/src/gpu/GrAAHairLinePathRenderer.cpp
|
| +++ b/src/gpu/GrAAHairLinePathRenderer.cpp
|
| @@ -29,6 +29,9 @@ namespace {
|
| static const int kVertsPerQuad = 5;
|
| static const int kIdxsPerQuad = 9;
|
|
|
| +static const int kVertsPerCubic = 4;
|
| +static const int kIdxsPerCubic = 6;
|
| +
|
| static const int kVertsPerLineSeg = 4;
|
| static const int kIdxsPerLineSeg = 6;
|
|
|
| @@ -37,6 +40,39 @@ static const size_t kQuadIdxSBufize = kIdxsPerQuad *
|
| sizeof(uint16_t) *
|
| kNumQuadsInIdxBuffer;
|
|
|
| +static const int kNumCubicsInIdxBuffer = 256;
|
| +static const size_t kCubicIdxSBufize = kIdxsPerCubic *
|
| + sizeof(uint16_t) *
|
| + kNumCubicsInIdxBuffer;
|
| +
|
| +bool push_cubic_index_data(GrIndexBuffer* cIdxBuffer) {
|
| + uint16_t* data = (uint16_t*) cIdxBuffer->lock();
|
| + bool tempData = NULL == data;
|
| + if (tempData) {
|
| + data = SkNEW_ARRAY(uint16_t, kNumCubicsInIdxBuffer * kIdxsPerCubic);
|
| + }
|
| + for (int i = 0; i < kNumCubicsInIdxBuffer; ++i) {
|
| +
|
| + int baseIdx = i * kIdxsPerCubic;
|
| + uint16_t baseVert = (uint16_t)(i * kVertsPerCubic);
|
| +
|
| + data[0 + baseIdx] = baseVert + 0;
|
| + data[1 + baseIdx] = baseVert + 1;
|
| + data[2 + baseIdx] = baseVert + 2;
|
| + data[3 + baseIdx] = baseVert + 2;
|
| + data[4 + baseIdx] = baseVert + 3;
|
| + data[5 + baseIdx] = baseVert + 0;
|
| + }
|
| + if (tempData) {
|
| + bool ret = cIdxBuffer->updateData(data, kCubicIdxSBufize);
|
| + delete[] data;
|
| + return ret;
|
| + } else {
|
| + cIdxBuffer->unlock();
|
| + return true;
|
| + }
|
| +}
|
| +
|
| bool push_quad_index_data(GrIndexBuffer* qIdxBuffer) {
|
| uint16_t* data = (uint16_t*) qIdxBuffer->lock();
|
| bool tempData = NULL == data;
|
| @@ -92,23 +128,33 @@ GrPathRenderer* GrAAHairLinePathRenderer::Create(GrContext* context) {
|
| !push_quad_index_data(qIdxBuf)) {
|
| return NULL;
|
| }
|
| + GrIndexBuffer* cIdxBuf = gpu->createIndexBuffer(kCubicIdxSBufize, false);
|
| + SkAutoTUnref<GrIndexBuffer> cIdxBuffer(cIdxBuf);
|
| + if (NULL == cIdxBuf ||
|
| + !push_cubic_index_data(cIdxBuf)) {
|
| + return NULL;
|
| + }
|
| return SkNEW_ARGS(GrAAHairLinePathRenderer,
|
| - (context, lIdxBuffer, qIdxBuf));
|
| + (context, lIdxBuffer, qIdxBuf, cIdxBuf));
|
| }
|
|
|
| GrAAHairLinePathRenderer::GrAAHairLinePathRenderer(
|
| const GrContext* context,
|
| const GrIndexBuffer* linesIndexBuffer,
|
| - const GrIndexBuffer* quadsIndexBuffer) {
|
| + const GrIndexBuffer* quadsIndexBuffer,
|
| + const GrIndexBuffer* cubicsIndexBuffer) {
|
| fLinesIndexBuffer = linesIndexBuffer;
|
| linesIndexBuffer->ref();
|
| fQuadsIndexBuffer = quadsIndexBuffer;
|
| quadsIndexBuffer->ref();
|
| + fCubicsIndexBuffer = cubicsIndexBuffer;
|
| + cubicsIndexBuffer->ref();
|
| }
|
|
|
| GrAAHairLinePathRenderer::~GrAAHairLinePathRenderer() {
|
| fLinesIndexBuffer->unref();
|
| fQuadsIndexBuffer->unref();
|
| + fCubicsIndexBuffer->unref();
|
| }
|
|
|
| namespace {
|
| @@ -142,6 +188,9 @@ int get_float_exp(float x) {
|
| return (((*iptr) & 0x7f800000) >> 23) - 127;
|
| }
|
|
|
| +//int chop_cubic_at_loop_intersection(const SkPoint src[4], SkPoint dst[10], SkScalar k[3] = NULL,
|
| +// SkScalar l[3] = NULL, SkScalar m[3] = NULL);
|
| +
|
| // Uses the max curvature function for quads to estimate
|
| // where to chop the conic. If the max curvature is not
|
| // found along the curve segment it will return 1 and
|
| @@ -179,6 +228,27 @@ int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
|
| return conicCnt;
|
| }
|
|
|
| +int is_degen_cubic(const SkPoint p[4]) {
|
| + static const SkScalar gDegenerateToLineTol = SK_Scalar1;
|
| + static const SkScalar gDegenerateToLineTolSqd =
|
| + SkScalarMul(gDegenerateToLineTol, gDegenerateToLineTol);
|
| +
|
| + if (p[0].distanceToSqd(p[3]) < gDegenerateToLineTolSqd &&
|
| + p[1].distanceToSqd(p[2]) < gDegenerateToLineTolSqd) {
|
| + return 1;
|
| + }
|
| +
|
| + // if the points are all close to being colinear
|
| + SkScalar dsqd1= p[1].distanceToLineBetweenSqd(p[0], p[3]);
|
| + SkScalar dsqd2= p[2].distanceToLineBetweenSqd(p[0], p[3]);
|
| + if (dsqd1 < gDegenerateToLineTolSqd && dsqd2 < gDegenerateToLineTolSqd) {
|
| + return 1;
|
| + }
|
| +
|
| + return 0;
|
| +
|
| +}
|
| +
|
| // returns 0 if quad/conic is degen or close to it
|
| // in this case approx the path with lines
|
| // otherwise returns 1
|
| @@ -269,8 +339,10 @@ int generate_lines_and_quads(const SkPath& path,
|
| PtArray* lines,
|
| PtArray* quads,
|
| PtArray* conics,
|
| + PtArray* cubics,
|
| IntArray* quadSubdivCnts,
|
| - FloatArray* conicWeights) {
|
| + FloatArray* conicWeights,
|
| + FloatArray* cubicKLM) {
|
| SkPath::Iter iter(path, false);
|
|
|
| int totalQuadCount = 0;
|
| @@ -367,63 +439,48 @@ int generate_lines_and_quads(const SkPath& path,
|
| }
|
| break;
|
| }
|
| - case SkPath::kCubic_Verb:
|
| + case SkPath::kCubic_Verb: {
|
| + SkPoint dst[10];
|
| + SkScalar klm[9];
|
| + SkScalar klm_rev[3];
|
| m.mapPoints(devPts, pathPts, 4);
|
| - bounds.setBounds(devPts, 4);
|
| - bounds.outset(SK_Scalar1, SK_Scalar1);
|
| - bounds.roundOut(&ibounds);
|
| - if (SkIRect::Intersects(devClipBounds, ibounds)) {
|
| - PREALLOC_PTARRAY(32) q;
|
| - // we don't need a direction if we aren't constraining the subdivision
|
| - static const SkPath::Direction kDummyDir = SkPath::kCCW_Direction;
|
| - // We convert cubics to quadratics (for now).
|
| - // In perspective have to do conversion in src space.
|
| - if (persp) {
|
| - SkScalar tolScale =
|
| - GrPathUtils::scaleToleranceToSrc(SK_Scalar1, m,
|
| - path.getBounds());
|
| - GrPathUtils::convertCubicToQuads(pathPts, tolScale, false, kDummyDir, &q);
|
| - } else {
|
| - GrPathUtils::convertCubicToQuads(devPts, SK_Scalar1, false, kDummyDir, &q);
|
| - }
|
| - for (int i = 0; i < q.count(); i += 3) {
|
| - SkPoint* qInDevSpace;
|
| - // bounds has to be calculated in device space, but q is
|
| - // in src space when there is perspective.
|
| - if (persp) {
|
| - m.mapPoints(devPts, &q[i], 3);
|
| - bounds.setBounds(devPts, 3);
|
| - qInDevSpace = devPts;
|
| + int cubicCnt = GrPathUtils::chopCubicAtLoopIntersection(pathPts, dst, klm,
|
| + klm_rev, devPts);
|
| + for (int i = 0; i < cubicCnt; ++i) {
|
| + SkPoint* chopPnts = &dst[i*3];
|
| + m.mapPoints(devPts, chopPnts, 4);
|
| + bounds.setBounds(devPts, 4);
|
| + bounds.outset(SK_Scalar1, SK_Scalar1);
|
| + bounds.roundOut(&ibounds);
|
| + if (SkIRect::Intersects(devClipBounds, ibounds)) {
|
| + if (is_degen_cubic(devPts)) {
|
| + SkPoint* pts = lines->push_back_n(6);
|
| + pts[0] = devPts[0];
|
| + pts[1] = devPts[1];
|
| + pts[2] = devPts[1];
|
| + pts[3] = devPts[2];
|
| + pts[4] = devPts[2];
|
| + pts[5] = devPts[3];
|
| } else {
|
| - bounds.setBounds(&q[i], 3);
|
| - qInDevSpace = &q[i];
|
| - }
|
| - bounds.outset(SK_Scalar1, SK_Scalar1);
|
| - bounds.roundOut(&ibounds);
|
| - if (SkIRect::Intersects(devClipBounds, ibounds)) {
|
| - int subdiv = num_quad_subdivs(qInDevSpace);
|
| - GrAssert(subdiv >= -1);
|
| - if (-1 == subdiv) {
|
| - SkPoint* pts = lines->push_back_n(4);
|
| - // lines should always be in device coords
|
| - pts[0] = qInDevSpace[0];
|
| - pts[1] = qInDevSpace[1];
|
| - pts[2] = qInDevSpace[1];
|
| - pts[3] = qInDevSpace[2];
|
| - } else {
|
| - SkPoint* pts = quads->push_back_n(3);
|
| - // q is already in src space when there is no
|
| - // perspective and dev coords otherwise.
|
| - pts[0] = q[0 + i];
|
| - pts[1] = q[1 + i];
|
| - pts[2] = q[2 + i];
|
| - quadSubdivCnts->push_back() = subdiv;
|
| - totalQuadCount += 1 << subdiv;
|
| + // when in perspective keep conics in src space
|
| + SkPoint* cPts = persp ? chopPnts : devPts;
|
| + SkPoint* pts = cubics->push_back_n(4);
|
| + pts[0] = cPts[0];
|
| + pts[1] = cPts[1];
|
| + pts[2] = cPts[2];
|
| + pts[3] = cPts[3];
|
| + SkScalar* klms = cubicKLM->push_back_n(9);
|
| + // set sub sections klm to equal klm of whole curve and
|
| + // flip the signs of k, l if needed
|
| + memcpy(klms, klm, 9 * sizeof(SkScalar));
|
| + for (int j = 0; j < 6; ++j) {
|
| + klms[j] *= klm_rev[i];
|
| }
|
| }
|
| }
|
| }
|
| break;
|
| + }
|
| case SkPath::kClose_Verb:
|
| break;
|
| case SkPath::kDone_Verb:
|
| @@ -444,7 +501,7 @@ struct Vertex {
|
| SkScalar fK;
|
| SkScalar fL;
|
| SkScalar fM;
|
| - } fConic;
|
| + } fBezier;
|
| GrVec fQuadCoord;
|
| struct {
|
| SkScalar fBogus[4];
|
| @@ -478,6 +535,66 @@ void set_uv_quad(const SkPoint qpts[3], Vertex verts[kVertsPerQuad]) {
|
| DevToUV.apply<kVertsPerQuad, sizeof(Vertex), sizeof(GrPoint)>(verts);
|
| }
|
|
|
| +// Set verts to be bounding box around the control points cpts bloated
|
| +// out by 1 in each direcition
|
| +void bloat_cubic(const SkPoint cpts[4], const SkMatrix* toDevice,
|
| + const SkMatrix* toSrc, Vertex verts[kVertsPerCubic],
|
| + SkRect* devBounds) {
|
| + GrAssert(!toDevice == !toSrc);
|
| + // original cubic is specified by quad a,b,c,d
|
| + SkPoint a = cpts[0];
|
| + SkPoint b = cpts[1];
|
| + SkPoint c = cpts[2];
|
| + SkPoint d = cpts[3];
|
| +
|
| + if (toDevice) {
|
| + toDevice->mapPoints(&a, 1);
|
| + toDevice->mapPoints(&b, 1);
|
| + toDevice->mapPoints(&c, 1);
|
| + toDevice->mapPoints(&d, 1);
|
| + }
|
| +
|
| + SkScalar minX = cpts[0].fX;
|
| + SkScalar maxX = cpts[0].fX;
|
| + SkScalar minY = cpts[0].fY;
|
| + SkScalar maxY = cpts[0].fY;
|
| +
|
| + for (int i = 1; i < 4; ++i) {
|
| + minX = SkMinScalar(minX, cpts[i].fX);
|
| + maxX = SkMaxScalar(maxX, cpts[i].fX);
|
| + minY = SkMinScalar(minY, cpts[i].fY);
|
| + maxY = SkMaxScalar(maxY, cpts[i].fY);
|
| + }
|
| +
|
| + minX -= 1.0f;
|
| + maxX += 1.0f;
|
| + minY -= 1.0f;
|
| + maxY += 1.0f;
|
| +
|
| + Vertex& a1 = verts[0];
|
| + Vertex& b1 = verts[1];
|
| + Vertex& c1 = verts[2];
|
| + Vertex& d1 = verts[3];
|
| +
|
| + a1.fPos.fX = minX;
|
| + a1.fPos.fY = minY;
|
| + b1.fPos.fX = maxX;
|
| + b1.fPos.fY = minY;
|
| + c1.fPos.fX = maxX;
|
| + c1.fPos.fY = maxY;
|
| + d1.fPos.fX = minX;
|
| + d1.fPos.fY = maxY;
|
| +
|
| + devBounds->growToInclude(a1.fPos.fX, a1.fPos.fY);
|
| + devBounds->growToInclude(b1.fPos.fX, b1.fPos.fY);
|
| + devBounds->growToInclude(c1.fPos.fX, c1.fPos.fY);
|
| + devBounds->growToInclude(d1.fPos.fX, d1.fPos.fY);
|
| +
|
| + if (toSrc) {
|
| + toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(Vertex), kVertsPerCubic);
|
| + }
|
| +}
|
| +
|
| void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice,
|
| const SkMatrix* toSrc, Vertex verts[kVertsPerQuad],
|
| SkRect* devBounds) {
|
| @@ -584,16 +701,16 @@ void calc_conic_klm(const SkPoint p[3], const SkScalar weight,
|
| scale = SkMaxScalar(scale, SkScalarAbs(m[i]));
|
| }
|
| GrAssert(scale > 0);
|
| - scale /= 10.0f;
|
| - k[0] /= scale;
|
| - k[1] /= scale;
|
| - k[2] /= scale;
|
| - l[0] /= scale;
|
| - l[1] /= scale;
|
| - l[2] /= scale;
|
| - m[0] /= scale;
|
| - m[1] /= scale;
|
| - m[2] /= scale;
|
| + scale = 10.0f / scale;
|
| + k[0] *= scale;
|
| + k[1] *= scale;
|
| + k[2] *= scale;
|
| + l[0] *= scale;
|
| + l[1] *= scale;
|
| + l[2] *= scale;
|
| + m[0] *= scale;
|
| + m[1] *= scale;
|
| + m[2] *= scale;
|
| }
|
|
|
| // Equations based off of Loop-Blinn Quadratic GPU Rendering
|
| @@ -603,7 +720,7 @@ void calc_conic_klm(const SkPoint p[3], const SkScalar weight,
|
| // f(x, y, w) = f(P) = K^2 - LM
|
| // K = dot(k, P), L = dot(l, P), M = dot(m, P)
|
| // k, l, m are calculated in function calc_conic_klm
|
| -void set_conic_coeffs(const SkPoint p[3], Vertex verts[kVertsPerQuad], const float weight) {
|
| +void set_conic_klm(const SkPoint p[3], Vertex verts[kVertsPerQuad], const float weight) {
|
| SkScalar k[3];
|
| SkScalar l[3];
|
| SkScalar m[3];
|
| @@ -612,12 +729,32 @@ void set_conic_coeffs(const SkPoint p[3], Vertex verts[kVertsPerQuad], const flo
|
|
|
| for (int i = 0; i < kVertsPerQuad; ++i) {
|
| const SkPoint pnt = verts[i].fPos;
|
| - verts[i].fConic.fK = pnt.fX * k[0] + pnt.fY * k[1] + k[2];
|
| - verts[i].fConic.fL = pnt.fX * l[0] + pnt.fY * l[1] + l[2];
|
| - verts[i].fConic.fM = pnt.fX * m[0] + pnt.fY * m[1] + m[2];
|
| + verts[i].fBezier.fK = pnt.fX * k[0] + pnt.fY * k[1] + k[2];
|
| + verts[i].fBezier.fL = pnt.fX * l[0] + pnt.fY * l[1] + l[2];
|
| + verts[i].fBezier.fM = pnt.fX * m[0] + pnt.fY * m[1] + m[2];
|
| + }
|
| +}
|
| +
|
| +void set_cubic_klm(const SkPoint p[4], Vertex verts[kVertsPerCubic], const SkScalar klm[9]) {
|
| + for (int i = 0; i < kVertsPerCubic; ++i) {
|
| + const SkPoint pnt = verts[i].fPos;
|
| + verts[i].fBezier.fK = pnt.fX * klm[0] + pnt.fY * klm[1] + klm[2];
|
| + verts[i].fBezier.fL = pnt.fX * klm[3] + pnt.fY * klm[4] + klm[5];
|
| + verts[i].fBezier.fM = pnt.fX * klm[6] + pnt.fY * klm[7] + klm[8];
|
| }
|
| }
|
|
|
| +void add_cubics(const SkPoint p[3],
|
| + const SkScalar klm[9],
|
| + const SkMatrix* toDevice,
|
| + const SkMatrix* toSrc,
|
| + Vertex** vert,
|
| + SkRect* devBounds) {
|
| + bloat_cubic(p, toDevice, toSrc, *vert, devBounds);
|
| + set_cubic_klm(p, *vert, klm);
|
| + *vert += kVertsPerCubic;
|
| +}
|
| +
|
| void add_conics(const SkPoint p[3],
|
| float weight,
|
| const SkMatrix* toDevice,
|
| @@ -625,7 +762,7 @@ void add_conics(const SkPoint p[3],
|
| Vertex** vert,
|
| SkRect* devBounds) {
|
| bloat_quad(p, toDevice, toSrc, *vert, devBounds);
|
| - set_conic_coeffs(p, *vert, weight);
|
| + set_conic_klm(p, *vert, weight);
|
| *vert += kVertsPerQuad;
|
| }
|
|
|
| @@ -692,6 +829,105 @@ void add_line(const SkPoint p[2],
|
| }
|
|
|
| /**
|
| + * Shader is based off of "Resolution Independent Curve Rendering using
|
| + * Programmable Graphics Hardware" by Loop and Blinn.
|
| + * The output of this effect is a hairline edge for non rational cubics.
|
| + * Cubics are specified by implicit equation K^3 - LM.
|
| + * K, L, and M, are the first three values of the vertex attribute,
|
| + * the fourth value is not used. Distance is calculated using a
|
| + * first order approximation from the taylor series.
|
| + * Coverage is max(0, 1-distance).
|
| + */
|
| +class HairCubicEdgeEffect : public GrEffect {
|
| +public:
|
| + static GrEffectRef* Create() {
|
| + GR_CREATE_STATIC_EFFECT(gHairCubicEdgeEffect, HairCubicEdgeEffect, ());
|
| + gHairCubicEdgeEffect->ref();
|
| + return gHairCubicEdgeEffect;
|
| + }
|
| +
|
| + virtual ~HairCubicEdgeEffect() {}
|
| +
|
| + static const char* Name() { return "HairCubicEdge"; }
|
| +
|
| + virtual void getConstantColorComponents(GrColor* color,
|
| + uint32_t* validFlags) const SK_OVERRIDE {
|
| + *validFlags = 0;
|
| + }
|
| +
|
| + virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
|
| + return GrTBackendEffectFactory<HairCubicEdgeEffect>::getInstance();
|
| + }
|
| +
|
| + class GLEffect : public GrGLEffect {
|
| + public:
|
| + GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&)
|
| + : INHERITED (factory) {}
|
| +
|
| + virtual void emitCode(GrGLShaderBuilder* builder,
|
| + const GrDrawEffect& drawEffect,
|
| + EffectKey key,
|
| + const char* outputColor,
|
| + const char* inputColor,
|
| + const TextureSamplerArray& samplers) SK_OVERRIDE {
|
| + const char *vsName, *fsName;
|
| +
|
| + SkAssertResult(builder->enableFeature(
|
| + GrGLShaderBuilder::kStandardDerivatives_GLSLFeature));
|
| + builder->addVarying(kVec4f_GrSLType, "CubicCoeffs",
|
| + &vsName, &fsName);
|
| + const SkString* attr0Name =
|
| + builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
|
| + builder->vsCodeAppendf("\t%s = %s;\n", vsName, attr0Name->c_str());
|
| +
|
| + builder->fsCodeAppend("\t\tfloat edgeAlpha;\n");
|
| +
|
| + builder->fsCodeAppendf("\t\tvec3 dklmdx = dFdx(%s.xyz);\n", fsName);
|
| + builder->fsCodeAppendf("\t\tvec3 dklmdy = dFdy(%s.xyz);\n", fsName);
|
| + builder->fsCodeAppendf("\t\tfloat dfdx =\n"
|
| + "\t\t3.0*%s.x*%s.x*dklmdx.x - %s.y*dklmdx.z - %s.z*dklmdx.y;\n",
|
| + fsName, fsName, fsName, fsName);
|
| + builder->fsCodeAppendf("\t\tfloat dfdy =\n"
|
| + "\t\t3.0*%s.x*%s.x*dklmdy.x - %s.y*dklmdy.z - %s.z*dklmdy.y;\n",
|
| + fsName, fsName, fsName, fsName);
|
| + builder->fsCodeAppend("\t\tvec2 gF = vec2(dfdx, dfdy);\n");
|
| + builder->fsCodeAppend("\t\tfloat gFM = sqrt(dot(gF, gF));\n");
|
| + builder->fsCodeAppendf("\t\tfloat func = abs(%s.x*%s.x*%s.x - %s.y*%s.z);\n",
|
| + fsName, fsName, fsName, fsName, fsName);
|
| + builder->fsCodeAppend("\t\tedgeAlpha = func / gFM;\n");
|
| + builder->fsCodeAppend("\t\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
|
| + // Add line below for smooth cubic ramp
|
| + // builder->fsCodeAppend("\t\tedgeAlpha = edgeAlpha*edgeAlpha*(3.0-2.0*edgeAlpha);\n");
|
| +
|
| + SkString modulate;
|
| + GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha");
|
| + builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str());
|
| + }
|
| +
|
| + static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
|
| + return 0x0;
|
| + }
|
| +
|
| + virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {}
|
| +
|
| + private:
|
| + typedef GrGLEffect INHERITED;
|
| + };
|
| +
|
| +private:
|
| + HairCubicEdgeEffect() {
|
| + this->addVertexAttrib(kVec4f_GrSLType);
|
| + }
|
| +
|
| + virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
|
| + return true;
|
| + }
|
| +
|
| + GR_DECLARE_EFFECT_TEST;
|
| +
|
| + typedef GrEffect INHERITED;
|
| +};
|
| +/**
|
| * Shader is based off of Loop-Blinn Quadratic GPU Rendering
|
| * The output of this effect is a hairline edge for conics.
|
| * Conics specified by implicit equation K^2 - LM.
|
| @@ -1040,6 +1276,7 @@ bool GrAAHairLinePathRenderer::createGeom(
|
| int* lineCnt,
|
| int* quadCnt,
|
| int* conicCnt,
|
| + int* cubicCnt,
|
| GrDrawTarget::AutoReleaseGeometry* arg,
|
| SkRect* devBounds) {
|
| GrDrawState* drawState = target->drawState();
|
| @@ -1060,15 +1297,19 @@ bool GrAAHairLinePathRenderer::createGeom(
|
| PREALLOC_PTARRAY(128) lines;
|
| PREALLOC_PTARRAY(128) quads;
|
| PREALLOC_PTARRAY(128) conics;
|
| + PREALLOC_PTARRAY(128) cubics;
|
| IntArray qSubdivs;
|
| FloatArray cWeights;
|
| + FloatArray cubicKLM;
|
| *quadCnt = generate_lines_and_quads(path, viewM, devClipBounds,
|
| - &lines, &quads, &conics, &qSubdivs, &cWeights);
|
| + &lines, &quads, &conics, &cubics,
|
| + &qSubdivs, &cWeights, &cubicKLM);
|
|
|
| *lineCnt = lines.count() / 2;
|
| *conicCnt = conics.count() / 3;
|
| + *cubicCnt = cubics.count() / 4;
|
| int vertCnt = kVertsPerLineSeg * *lineCnt + kVertsPerQuad * *quadCnt +
|
| - kVertsPerQuad * *conicCnt;
|
| + kVertsPerQuad * *conicCnt + kVertsPerCubic * *cubicCnt;
|
|
|
| target->drawState()->setVertexAttribs<gHairlineAttribs>(SK_ARRAY_COUNT(gHairlineAttribs));
|
| GrAssert(sizeof(Vertex) == target->getDrawState().getVertexSize());
|
| @@ -1100,10 +1341,13 @@ bool GrAAHairLinePathRenderer::createGeom(
|
| add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts, devBounds);
|
| }
|
|
|
| - // Start Conics
|
| for (int i = 0; i < *conicCnt; ++i) {
|
| add_conics(&conics[3*i], cWeights[i], toDevice, toSrc, &verts, devBounds);
|
| }
|
| +
|
| + for (int i = 0; i < *cubicCnt; ++i) {
|
| + add_cubics(&cubics[4*i], &cubicKLM[9*i], toDevice, toSrc, &verts, devBounds);
|
| + }
|
| return true;
|
| }
|
|
|
| @@ -1130,6 +1374,7 @@ bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path,
|
| int lineCnt;
|
| int quadCnt;
|
| int conicCnt;
|
| + int cubicCnt;
|
| GrDrawTarget::AutoReleaseGeometry arg;
|
| SkRect devBounds;
|
|
|
| @@ -1138,6 +1383,7 @@ bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path,
|
| &lineCnt,
|
| &quadCnt,
|
| &conicCnt,
|
| + &cubicCnt,
|
| &arg,
|
| &devBounds)) {
|
| return false;
|
| @@ -1162,6 +1408,7 @@ bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path,
|
| GrEffectRef* hairLineEffect = HairLineEdgeEffect::Create();
|
| GrEffectRef* hairQuadEffect = HairQuadEdgeEffect::Create();
|
| GrEffectRef* hairConicEffect = HairConicEdgeEffect::Create();
|
| + GrEffectRef* hairCubicEffect = HairCubicEdgeEffect::Create();
|
|
|
| // Check devBounds
|
| #if GR_DEBUG
|
| @@ -1228,7 +1475,7 @@ bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path,
|
| {
|
| GrDrawState::AutoRestoreEffects are(drawState);
|
| int conics = 0;
|
| - drawState->addCoverageEffect(hairConicEffect, 1, 2)->unref();
|
| + drawState->addCoverageEffect(hairConicEffect, kEdgeAttrIndex)->unref();
|
| while (conics < conicCnt) {
|
| int n = GrMin(conicCnt - conics, kNumQuadsInIdxBuffer);
|
| target->drawIndexed(kTriangles_GrPrimitiveType,
|
| @@ -1241,6 +1488,25 @@ bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path,
|
| conics += n;
|
| }
|
| }
|
| +
|
| + {
|
| + GrDrawState::AutoRestoreEffects are(drawState);
|
| + target->setIndexSourceToBuffer(fCubicsIndexBuffer);
|
| + int cubics = 0;
|
| + drawState->addCoverageEffect(hairCubicEffect, kEdgeAttrIndex)->unref();
|
| + while (cubics < cubicCnt) {
|
| + int n = GrMin(cubicCnt - cubics, kNumCubicsInIdxBuffer);
|
| + target->drawIndexed(kTriangles_GrPrimitiveType,
|
| + kVertsPerLineSeg * lineCnt +
|
| + kVertsPerQuad * (quadCnt + conicCnt) +
|
| + kVertsPerCubic * cubics, // startV
|
| + 0, // startI
|
| + kVertsPerCubic*n, // vCount
|
| + kIdxsPerCubic*n, // iCount
|
| + &devBounds);
|
| + cubics += n;
|
| + }
|
| + }
|
| target->resetIndexSource();
|
|
|
| return true;
|
|
|