blob: b6612d1d293f67e8ccede8126a663f6ee92ab532 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000010#include "GrTesselatedPathRenderer.h"
11
tomhudson@google.com93813632011-10-27 20:21:16 +000012#include "GrDrawState.h"
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000013#include "GrPathUtils.h"
reed@google.com07f3ee12011-05-16 17:21:57 +000014#include "GrPoint.h"
15#include "GrTDArray.h"
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000016
bsalomon@google.com3582bf92011-06-30 21:32:31 +000017#include "SkTemplates.h"
18
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000019#include <limits.h>
senorblanco@chromium.org1fa803d2011-05-25 14:46:17 +000020#include <sk_glu.h>
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000021
tomhudson@google.com93813632011-10-27 20:21:16 +000022typedef GrTDArray<GrDrawState::Edge> GrEdgeArray;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000023typedef GrTDArray<GrPoint> GrPointArray;
24typedef GrTDArray<uint16_t> GrIndexArray;
25typedef void (*TESSCB)();
26
27// limit the allowable vertex range to approximately half of the representable
28// IEEE exponent in order to avoid overflow when doing multiplies between
29// vertex components,
bsalomon@google.comee435122011-07-01 14:57:55 +000030const float kMaxVertexValue = 1e18f;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000031
tomhudson@google.com93813632011-10-27 20:21:16 +000032static inline GrDrawState::Edge computeEdge(const GrPoint& p,
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000033 const GrPoint& q,
34 float sign) {
35 GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
36 float scale = sign / tangent.length();
37 float cross2 = p.fX * q.fY - q.fX * p.fY;
tomhudson@google.com93813632011-10-27 20:21:16 +000038 return GrDrawState::Edge(tangent.fX * scale,
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000039 tangent.fY * scale,
40 cross2 * scale);
41}
42
43static inline GrPoint sanitizePoint(const GrPoint& pt) {
44 GrPoint r;
45 r.fX = SkScalarPin(pt.fX, -kMaxVertexValue, kMaxVertexValue);
46 r.fY = SkScalarPin(pt.fY, -kMaxVertexValue, kMaxVertexValue);
47 return r;
48}
49
50class GrTess {
51public:
52 GrTess(int count, unsigned winding_rule) {
53 fTess = Sk_gluNewTess();
54 Sk_gluTessProperty(fTess, GLU_TESS_WINDING_RULE, winding_rule);
55 Sk_gluTessNormal(fTess, 0.0f, 0.0f, 1.0f);
56 Sk_gluTessCallback(fTess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginCB);
57 Sk_gluTessCallback(fTess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexCB);
58 Sk_gluTessCallback(fTess, GLU_TESS_END_DATA, (TESSCB) &endCB);
59 Sk_gluTessCallback(fTess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagCB);
60 Sk_gluTessCallback(fTess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineCB);
61 fInVertices = new double[count * 3];
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000062 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000063 ~GrTess() {
64 Sk_gluDeleteTess(fTess);
65 delete[] fInVertices;
66 }
67 void addVertex(const GrPoint& pt, int index) {
68 if (index > USHRT_MAX) return;
69 double* inVertex = &fInVertices[index * 3];
70 inVertex[0] = pt.fX;
71 inVertex[1] = pt.fY;
72 inVertex[2] = 0.0;
73 *fVertices.append() = pt;
74 Sk_gluTessVertex(fTess, inVertex, reinterpret_cast<void*>(index));
75 }
76 void addVertices(const GrPoint* points, const uint16_t* contours, int numContours) {
77 Sk_gluTessBeginPolygon(fTess, this);
78 size_t i = 0;
79 for (int j = 0; j < numContours; ++j) {
80 Sk_gluTessBeginContour(fTess);
81 size_t end = i + contours[j];
82 for (; i < end; ++i) {
83 addVertex(points[i], i);
84 }
85 Sk_gluTessEndContour(fTess);
86 }
87 Sk_gluTessEndPolygon(fTess);
88 }
89 GLUtesselator* tess() { return fTess; }
90 const GrPointArray& vertices() const { return fVertices; }
91protected:
92 virtual void begin(GLenum type) = 0;
93 virtual void vertex(int index) = 0;
94 virtual void edgeFlag(bool flag) = 0;
95 virtual void end() = 0;
96 virtual int combine(GLdouble coords[3], int vertexIndices[4],
97 GLfloat weight[4]) = 0;
98 static void beginCB(GLenum type, void* data) {
99 static_cast<GrTess*>(data)->begin(type);
100 }
101 static void vertexCB(void* vertexData, void* data) {
102 static_cast<GrTess*>(data)->vertex(reinterpret_cast<long>(vertexData));
103 }
104 static void edgeFlagCB(GLboolean flag, void* data) {
105 static_cast<GrTess*>(data)->edgeFlag(flag != 0);
106 }
107 static void endCB(void* data) {
108 static_cast<GrTess*>(data)->end();
109 }
110 static void combineCB(GLdouble coords[3], void* vertexData[4],
111 GLfloat weight[4], void **outData, void* data) {
112 int vertexIndex[4];
113 vertexIndex[0] = reinterpret_cast<long>(vertexData[0]);
114 vertexIndex[1] = reinterpret_cast<long>(vertexData[1]);
115 vertexIndex[2] = reinterpret_cast<long>(vertexData[2]);
116 vertexIndex[3] = reinterpret_cast<long>(vertexData[3]);
117 GrTess* tess = static_cast<GrTess*>(data);
118 int outIndex = tess->combine(coords, vertexIndex, weight);
119 *reinterpret_cast<long*>(outData) = outIndex;
120 }
121protected:
122 GLUtesselator* fTess;
123 GrPointArray fVertices;
124 double* fInVertices;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000125};
126
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000127class GrPolygonTess : public GrTess {
128public:
129 GrPolygonTess(int count, unsigned winding_rule)
130 : GrTess(count, winding_rule) {
131 }
132 ~GrPolygonTess() {
133 }
134 const GrIndexArray& indices() const { return fIndices; }
135protected:
136 virtual void begin(GLenum type) {
137 GR_DEBUGASSERT(type == GL_TRIANGLES);
138 }
139 virtual void vertex(int index) {
140 *fIndices.append() = index;
141 }
142 virtual void edgeFlag(bool flag) {}
143 virtual void end() {}
144 virtual int combine(GLdouble coords[3], int vertexIndices[4],
145 GLfloat weight[4]) {
146 int index = fVertices.count();
147 GrPoint p = GrPoint::Make(static_cast<float>(coords[0]),
148 static_cast<float>(coords[1]));
149 *fVertices.append() = p;
150 return index;
151 }
152protected:
153 GrIndexArray fIndices;
154};
155
156class GrEdgePolygonTess : public GrPolygonTess {
157public:
158 GrEdgePolygonTess(int count, unsigned winding_rule, const SkMatrix& matrix)
159 : GrPolygonTess(count, winding_rule),
160 fMatrix(matrix),
161 fEdgeFlag(false),
162 fEdgeVertex(-1),
163 fTriStartVertex(-1),
164 fEdges(NULL) {
165 }
166 ~GrEdgePolygonTess() {
167 delete[] fEdges;
168 }
tomhudson@google.com93813632011-10-27 20:21:16 +0000169 const GrDrawState::Edge* edges() const { return fEdges; }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000170private:
171 void addEdge(int index0, int index1) {
172 GrPoint p = fVertices[index0];
173 GrPoint q = fVertices[index1];
174 fMatrix.mapPoints(&p, 1);
175 fMatrix.mapPoints(&q, 1);
176 p = sanitizePoint(p);
177 q = sanitizePoint(q);
178 if (p == q) return;
tomhudson@google.com93813632011-10-27 20:21:16 +0000179 GrDrawState::Edge edge = computeEdge(p, q, 1.0f);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000180 fEdges[index0 * 2 + 1] = edge;
181 fEdges[index1 * 2] = edge;
182 }
183 virtual void begin(GLenum type) {
184 GR_DEBUGASSERT(type == GL_TRIANGLES);
185 int count = fVertices.count() * 2;
tomhudson@google.com93813632011-10-27 20:21:16 +0000186 fEdges = new GrDrawState::Edge[count];
187 memset(fEdges, 0, count * sizeof(GrDrawState::Edge));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000188 }
189 virtual void edgeFlag(bool flag) {
190 fEdgeFlag = flag;
191 }
192 virtual void vertex(int index) {
193 bool triStart = fIndices.count() % 3 == 0;
194 GrPolygonTess::vertex(index);
195 if (fEdgeVertex != -1) {
196 if (triStart) {
197 addEdge(fEdgeVertex, fTriStartVertex);
198 } else {
199 addEdge(fEdgeVertex, index);
200 }
201 }
202 if (triStart) {
203 fTriStartVertex = index;
204 }
205 if (fEdgeFlag) {
206 fEdgeVertex = index;
207 } else {
208 fEdgeVertex = -1;
209 }
210 }
211 virtual void end() {
212 if (fEdgeVertex != -1) {
213 addEdge(fEdgeVertex, fTriStartVertex);
214 }
215 }
216 GrMatrix fMatrix;
217 bool fEdgeFlag;
218 int fEdgeVertex, fTriStartVertex;
tomhudson@google.com93813632011-10-27 20:21:16 +0000219 GrDrawState::Edge* fEdges;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000220};
221
222class GrBoundaryTess : public GrTess {
223public:
224 GrBoundaryTess(int count, unsigned winding_rule)
225 : GrTess(count, winding_rule),
226 fContourStart(0) {
227 Sk_gluTessProperty(fTess, GLU_TESS_BOUNDARY_ONLY, 1);
228 }
229 ~GrBoundaryTess() {
230 }
231 GrPointArray& contourPoints() { return fContourPoints; }
232 const GrIndexArray& contours() const { return fContours; }
233private:
234 virtual void begin(GLenum type) {
235 fContourStart = fContourPoints.count();
236 }
237 virtual void vertex(int index) {
238 *fContourPoints.append() = fVertices.at(index);
239 }
240 virtual void edgeFlag(bool flag) {}
241 virtual void end() {
242 *fContours.append() = fContourPoints.count() - fContourStart;
243 }
244 virtual int combine(GLdouble coords[3], int vertexIndices[4],
245 GLfloat weight[4]) {
246 int index = fVertices.count();
247 *fVertices.append() = GrPoint::Make(static_cast<float>(coords[0]),
248 static_cast<float>(coords[1]));
249 return index;
250 }
251 GrPointArray fContourPoints;
252 GrIndexArray fContours;
253 size_t fContourStart;
254};
255
256static bool nearlyEqual(float a, float b) {
257 return fabsf(a - b) < 0.0001f;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000258}
259
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000260static bool nearlyEqual(const GrPoint& a, const GrPoint& b) {
261 return nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000262}
263
tomhudson@google.com93813632011-10-27 20:21:16 +0000264static bool parallel(const GrDrawState::Edge& a, const GrDrawState::Edge& b) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000265 return (nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY)) ||
266 (nearlyEqual(a.fX, -b.fX) && nearlyEqual(a.fY, -b.fY));
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000267}
268
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000269static unsigned fill_type_to_glu_winding_rule(GrPathFill fill) {
270 switch (fill) {
271 case kWinding_PathFill:
272 return GLU_TESS_WINDING_NONZERO;
273 case kEvenOdd_PathFill:
274 return GLU_TESS_WINDING_ODD;
275 case kInverseWinding_PathFill:
276 return GLU_TESS_WINDING_POSITIVE;
277 case kInverseEvenOdd_PathFill:
278 return GLU_TESS_WINDING_ODD;
279 case kHairLine_PathFill:
280 return GLU_TESS_WINDING_NONZERO; // FIXME: handle this
281 default:
282 GrAssert(!"Unknown path fill!");
283 return 0;
284 }
285}
286
287GrTesselatedPathRenderer::GrTesselatedPathRenderer() {
288}
289
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000290static bool isCCW(const GrPoint* pts, int count) {
291 GrVec v1, v2;
292 do {
293 v1 = pts[1] - pts[0];
294 v2 = pts[2] - pts[1];
295 pts++;
296 count--;
297 } while (nearlyEqual(v1, v2) && count > 3);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000298 return v1.cross(v2) < 0;
299}
300
tomhudson@google.com93813632011-10-27 20:21:16 +0000301static bool validEdge(const GrDrawState::Edge& edge) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000302 return !(edge.fX == 0.0f && edge.fY == 0.0f && edge.fZ == 0.0f);
303}
304
305static size_t computeEdgesAndIntersect(const GrMatrix& matrix,
306 const GrMatrix& inverse,
307 GrPoint* vertices,
308 size_t numVertices,
309 GrEdgeArray* edges,
310 float sign) {
311 if (numVertices < 3) {
312 return 0;
313 }
senorblanco@chromium.orgff174b32011-05-16 16:59:57 +0000314 matrix.mapPoints(vertices, numVertices);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000315 if (sign == 0.0f) {
316 sign = isCCW(vertices, numVertices) ? -1.0f : 1.0f;
317 }
318 GrPoint p = sanitizePoint(vertices[numVertices - 1]);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000319 for (size_t i = 0; i < numVertices; ++i) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000320 GrPoint q = sanitizePoint(vertices[i]);
321 if (p == q) {
322 continue;
323 }
tomhudson@google.com93813632011-10-27 20:21:16 +0000324 GrDrawState::Edge edge = computeEdge(p, q, sign);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000325 edge.fZ += 0.5f; // Offset by half a pixel along the tangent.
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000326 *edges->append() = edge;
327 p = q;
328 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000329 int count = edges->count();
330 if (count == 0) {
331 return 0;
332 }
tomhudson@google.com93813632011-10-27 20:21:16 +0000333 GrDrawState::Edge prev_edge = edges->at(0);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000334 for (int i = 0; i < count; ++i) {
tomhudson@google.com93813632011-10-27 20:21:16 +0000335 GrDrawState::Edge edge = edges->at(i < count - 1 ? i + 1 : 0);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000336 if (parallel(edge, prev_edge)) {
337 // 3 points are collinear; offset by half the tangent instead
338 vertices[i].fX -= edge.fX * 0.5f;
339 vertices[i].fY -= edge.fY * 0.5f;
340 } else {
341 vertices[i] = prev_edge.intersect(edge);
342 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000343 inverse.mapPoints(&vertices[i], 1);
344 prev_edge = edge;
345 }
346 return edges->count();
347}
348
bsalomon@google.com39ee0ff2011-12-06 15:32:52 +0000349void GrTesselatedPathRenderer::drawPath(GrDrawState::StageMask stageMask) {
bsalomon@google.comee435122011-07-01 14:57:55 +0000350 GrDrawTarget::AutoStateRestore asr(fTarget);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000351 // face culling doesn't make sense here
tomhudson@google.com93813632011-10-27 20:21:16 +0000352 GrAssert(GrDrawState::kBoth_DrawFace == fTarget->getDrawFace());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000353
bsalomon@google.comee435122011-07-01 14:57:55 +0000354 GrMatrix viewM = fTarget->getViewMatrix();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000355
bsalomon@google.com181e9bd2011-09-07 18:42:30 +0000356 GrScalar tol = GR_Scalar1;
bsalomon@google.com38396322011-09-09 19:32:04 +0000357 tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, fPath->getBounds());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000358 GrScalar tolSqd = GrMul(tol, tol);
359
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000360 int subpathCnt;
bsalomon@google.comee435122011-07-01 14:57:55 +0000361 int maxPts = GrPathUtils::worstCasePointCount(*fPath, &subpathCnt, tol);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000362
363 GrVertexLayout layout = 0;
tomhudson@google.com93813632011-10-27 20:21:16 +0000364 for (int s = 0; s < GrDrawState::kNumStages; ++s) {
bsalomon@google.com39ee0ff2011-12-06 15:32:52 +0000365 if ((1 << s) & stageMask) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000366 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
367 }
368 }
369
bsalomon@google.comfa6ac932011-10-05 19:57:55 +0000370 bool inverted = GrIsFillInverted(fFill);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000371 if (inverted) {
372 maxPts += 4;
373 subpathCnt++;
374 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000375 if (maxPts > USHRT_MAX) {
376 return;
377 }
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000378 SkAutoSTMalloc<8, GrPoint> baseMem(maxPts);
379 GrPoint* base = baseMem;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000380 GrPoint* vert = base;
381 GrPoint* subpathBase = base;
382
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000383 SkAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000384
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000385 GrPoint pts[4];
bsalomon@google.comee435122011-07-01 14:57:55 +0000386 SkPath::Iter iter(*fPath, false);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000387
388 bool first = true;
389 int subpath = 0;
390
391 for (;;) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000392 switch (iter.next(pts)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000393 case kMove_PathCmd:
394 if (!first) {
395 subpathVertCount[subpath] = vert-subpathBase;
396 subpathBase = vert;
397 ++subpath;
398 }
399 *vert = pts[0];
400 vert++;
401 break;
402 case kLine_PathCmd:
403 *vert = pts[1];
404 vert++;
405 break;
406 case kQuadratic_PathCmd: {
407 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
408 tolSqd, &vert,
409 GrPathUtils::quadraticPointCount(pts, tol));
410 break;
411 }
412 case kCubic_PathCmd: {
413 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
414 tolSqd, &vert,
415 GrPathUtils::cubicPointCount(pts, tol));
416 break;
417 }
418 case kClose_PathCmd:
419 break;
420 case kEnd_PathCmd:
421 subpathVertCount[subpath] = vert-subpathBase;
422 ++subpath; // this could be only in debug
423 goto FINISHED;
424 }
425 first = false;
426 }
427FINISHED:
bsalomon@google.comee435122011-07-01 14:57:55 +0000428 if (0 != fTranslate.fX || 0 != fTranslate.fY) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000429 for (int i = 0; i < vert - base; i++) {
bsalomon@google.comee435122011-07-01 14:57:55 +0000430 base[i].offset(fTranslate.fX, fTranslate.fY);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000431 }
432 }
433
434 if (inverted) {
435 GrRect bounds;
bsalomon@google.comee435122011-07-01 14:57:55 +0000436 GrAssert(NULL != fTarget->getRenderTarget());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000437 bounds.setLTRB(0, 0,
bsalomon@google.comee435122011-07-01 14:57:55 +0000438 GrIntToScalar(fTarget->getRenderTarget()->width()),
439 GrIntToScalar(fTarget->getRenderTarget()->height()));
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000440 GrMatrix vmi;
bsalomon@google.comee435122011-07-01 14:57:55 +0000441 if (fTarget->getViewInverse(&vmi)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000442 vmi.mapRect(&bounds);
443 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000444 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
445 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
446 *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
447 *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000448 subpathVertCount[subpath++] = 4;
449 }
450
451 GrAssert(subpath == subpathCnt);
452 GrAssert((vert - base) <= maxPts);
453
454 size_t count = vert - base;
455
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000456 if (count < 3) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000457 return;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000458 }
459
bsalomon@google.comee435122011-07-01 14:57:55 +0000460 if (subpathCnt == 1 && !inverted && fPath->isConvex()) {
bsalomon@google.com289533a2011-10-27 12:34:25 +0000461 if (fAntiAlias) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000462 GrEdgeArray edges;
bsalomon@google.comee435122011-07-01 14:57:55 +0000463 GrMatrix inverse, matrix = fTarget->getViewMatrix();
464 fTarget->getViewInverse(&inverse);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000465
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000466 count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f);
bsalomon@google.comee435122011-07-01 14:57:55 +0000467 size_t maxEdges = fTarget->getMaxEdges();
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000468 if (count == 0) {
469 return;
470 }
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000471 if (count <= maxEdges) {
472 // All edges fit; upload all edges and draw all verts as a fan
bsalomon@google.comee435122011-07-01 14:57:55 +0000473 fTarget->setVertexSourceToArray(layout, base, count);
474 fTarget->setEdgeAAData(&edges[0], count);
475 fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000476 } else {
477 // Upload "maxEdges" edges and verts at a time, and draw as
478 // separate fans
479 for (size_t i = 0; i < count - 2; i += maxEdges - 2) {
480 edges[i] = edges[0];
481 base[i] = base[0];
482 int size = GR_CT_MIN(count - i, maxEdges);
bsalomon@google.comee435122011-07-01 14:57:55 +0000483 fTarget->setVertexSourceToArray(layout, &base[i], size);
484 fTarget->setEdgeAAData(&edges[i], size);
485 fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000486 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000487 }
bsalomon@google.comee435122011-07-01 14:57:55 +0000488 fTarget->setEdgeAAData(NULL, 0);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000489 } else {
bsalomon@google.comee435122011-07-01 14:57:55 +0000490 fTarget->setVertexSourceToArray(layout, base, count);
491 fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000492 }
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000493 return;
494 }
495
bsalomon@google.com289533a2011-10-27 12:34:25 +0000496 if (fAntiAlias) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000497 // Run the tesselator once to get the boundaries.
bsalomon@google.comee435122011-07-01 14:57:55 +0000498 GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fFill));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000499 btess.addVertices(base, subpathVertCount, subpathCnt);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000500
bsalomon@google.comee435122011-07-01 14:57:55 +0000501 GrMatrix inverse, matrix = fTarget->getViewMatrix();
502 if (!fTarget->getViewInverse(&inverse)) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000503 return;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000504 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000505
506 if (btess.vertices().count() > USHRT_MAX) {
507 return;
508 }
509
510 // Inflate the boundary, and run the tesselator again to generate
511 // interior polys.
512 const GrPointArray& contourPoints = btess.contourPoints();
513 const GrIndexArray& contours = btess.contours();
514 GrEdgePolygonTess ptess(contourPoints.count(), GLU_TESS_WINDING_NONZERO, matrix);
515
516 size_t i = 0;
517 Sk_gluTessBeginPolygon(ptess.tess(), &ptess);
518 for (int contour = 0; contour < contours.count(); ++contour) {
519 int count = contours[contour];
520 GrEdgeArray edges;
521 int newCount = computeEdgesAndIntersect(matrix, inverse, &btess.contourPoints()[i], count, &edges, 1.0f);
522 Sk_gluTessBeginContour(ptess.tess());
523 for (int j = 0; j < newCount; j++) {
524 ptess.addVertex(contourPoints[i + j], ptess.vertices().count());
525 }
526 i += count;
527 Sk_gluTessEndContour(ptess.tess());
528 }
529
530 Sk_gluTessEndPolygon(ptess.tess());
531
532 if (ptess.vertices().count() > USHRT_MAX) {
533 return;
534 }
535
536 // Draw the resulting polys and upload their edge data.
bsalomon@google.comee435122011-07-01 14:57:55 +0000537 fTarget->enableState(GrDrawTarget::kEdgeAAConcave_StateBit);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000538 const GrPointArray& vertices = ptess.vertices();
539 const GrIndexArray& indices = ptess.indices();
tomhudson@google.com93813632011-10-27 20:21:16 +0000540 const GrDrawState::Edge* edges = ptess.edges();
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000541 GR_DEBUGASSERT(indices.count() % 3 == 0);
542 for (int i = 0; i < indices.count(); i += 3) {
543 GrPoint tri_verts[3];
544 int index0 = indices[i];
545 int index1 = indices[i + 1];
546 int index2 = indices[i + 2];
547 tri_verts[0] = vertices[index0];
548 tri_verts[1] = vertices[index1];
549 tri_verts[2] = vertices[index2];
tomhudson@google.com93813632011-10-27 20:21:16 +0000550 GrDrawState::Edge tri_edges[6];
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000551 int t = 0;
tomhudson@google.com93813632011-10-27 20:21:16 +0000552 const GrDrawState::Edge& edge0 = edges[index0 * 2];
553 const GrDrawState::Edge& edge1 = edges[index0 * 2 + 1];
554 const GrDrawState::Edge& edge2 = edges[index1 * 2];
555 const GrDrawState::Edge& edge3 = edges[index1 * 2 + 1];
556 const GrDrawState::Edge& edge4 = edges[index2 * 2];
557 const GrDrawState::Edge& edge5 = edges[index2 * 2 + 1];
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000558 if (validEdge(edge0) && validEdge(edge1)) {
559 tri_edges[t++] = edge0;
560 tri_edges[t++] = edge1;
561 }
562 if (validEdge(edge2) && validEdge(edge3)) {
563 tri_edges[t++] = edge2;
564 tri_edges[t++] = edge3;
565 }
566 if (validEdge(edge4) && validEdge(edge5)) {
567 tri_edges[t++] = edge4;
568 tri_edges[t++] = edge5;
569 }
bsalomon@google.comee435122011-07-01 14:57:55 +0000570 fTarget->setEdgeAAData(&tri_edges[0], t);
571 fTarget->setVertexSourceToArray(layout, &tri_verts[0], 3);
572 fTarget->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000573 }
bsalomon@google.comee435122011-07-01 14:57:55 +0000574 fTarget->setEdgeAAData(NULL, 0);
575 fTarget->disableState(GrDrawTarget::kEdgeAAConcave_StateBit);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000576 return;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000577 }
578
bsalomon@google.comee435122011-07-01 14:57:55 +0000579 GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fFill));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000580 ptess.addVertices(base, subpathVertCount, subpathCnt);
581 const GrPointArray& vertices = ptess.vertices();
582 const GrIndexArray& indices = ptess.indices();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000583 if (indices.count() > 0) {
bsalomon@google.comee435122011-07-01 14:57:55 +0000584 fTarget->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
585 fTarget->setIndexSourceToArray(indices.begin(), indices.count());
586 fTarget->drawIndexed(kTriangles_PrimitiveType,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000587 0,
588 0,
589 vertices.count(),
590 indices.count());
591 }
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000592}
593
bsalomon@google.com289533a2011-10-27 12:34:25 +0000594bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget::Caps& caps,
bsalomon@google.comaeb21602011-08-30 18:13:44 +0000595 const SkPath& path,
bsalomon@google.com289533a2011-10-27 12:34:25 +0000596 GrPathFill fill,
597 bool antiAlias) const {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000598 return kHairLine_PathFill != fill;
599}
600
bsalomon@google.comee435122011-07-01 14:57:55 +0000601void GrTesselatedPathRenderer::drawPathToStencil() {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000602 GrAlwaysAssert(!"multipass stencil should not be needed");
603}
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000604