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; |