blob: 9a721016e6666652bfb43672ab670873d723dade [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
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000012#include "GrPathUtils.h"
reed@google.com07f3ee12011-05-16 17:21:57 +000013#include "GrPoint.h"
14#include "GrTDArray.h"
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000015
bsalomon@google.com3582bf92011-06-30 21:32:31 +000016#include "SkTemplates.h"
17
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000018#include <limits.h>
senorblanco@chromium.org1fa803d2011-05-25 14:46:17 +000019#include <sk_glu.h>
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000020
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000021typedef GrTDArray<GrDrawTarget::Edge> GrEdgeArray;
22typedef GrTDArray<GrPoint> GrPointArray;
23typedef GrTDArray<uint16_t> GrIndexArray;
24typedef void (*TESSCB)();
25
26// limit the allowable vertex range to approximately half of the representable
27// IEEE exponent in order to avoid overflow when doing multiplies between
28// vertex components,
bsalomon@google.comee435122011-07-01 14:57:55 +000029const float kMaxVertexValue = 1e18f;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000030
31static inline GrDrawTarget::Edge computeEdge(const GrPoint& p,
32 const GrPoint& q,
33 float sign) {
34 GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
35 float scale = sign / tangent.length();
36 float cross2 = p.fX * q.fY - q.fX * p.fY;
37 return GrDrawTarget::Edge(tangent.fX * scale,
38 tangent.fY * scale,
39 cross2 * scale);
40}
41
42static inline GrPoint sanitizePoint(const GrPoint& pt) {
43 GrPoint r;
44 r.fX = SkScalarPin(pt.fX, -kMaxVertexValue, kMaxVertexValue);
45 r.fY = SkScalarPin(pt.fY, -kMaxVertexValue, kMaxVertexValue);
46 return r;
47}
48
49class GrTess {
50public:
51 GrTess(int count, unsigned winding_rule) {
52 fTess = Sk_gluNewTess();
53 Sk_gluTessProperty(fTess, GLU_TESS_WINDING_RULE, winding_rule);
54 Sk_gluTessNormal(fTess, 0.0f, 0.0f, 1.0f);
55 Sk_gluTessCallback(fTess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginCB);
56 Sk_gluTessCallback(fTess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexCB);
57 Sk_gluTessCallback(fTess, GLU_TESS_END_DATA, (TESSCB) &endCB);
58 Sk_gluTessCallback(fTess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagCB);
59 Sk_gluTessCallback(fTess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineCB);
60 fInVertices = new double[count * 3];
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000061 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000062 ~GrTess() {
63 Sk_gluDeleteTess(fTess);
64 delete[] fInVertices;
65 }
66 void addVertex(const GrPoint& pt, int index) {
67 if (index > USHRT_MAX) return;
68 double* inVertex = &fInVertices[index * 3];
69 inVertex[0] = pt.fX;
70 inVertex[1] = pt.fY;
71 inVertex[2] = 0.0;
72 *fVertices.append() = pt;
73 Sk_gluTessVertex(fTess, inVertex, reinterpret_cast<void*>(index));
74 }
75 void addVertices(const GrPoint* points, const uint16_t* contours, int numContours) {
76 Sk_gluTessBeginPolygon(fTess, this);
77 size_t i = 0;
78 for (int j = 0; j < numContours; ++j) {
79 Sk_gluTessBeginContour(fTess);
80 size_t end = i + contours[j];
81 for (; i < end; ++i) {
82 addVertex(points[i], i);
83 }
84 Sk_gluTessEndContour(fTess);
85 }
86 Sk_gluTessEndPolygon(fTess);
87 }
88 GLUtesselator* tess() { return fTess; }
89 const GrPointArray& vertices() const { return fVertices; }
90protected:
91 virtual void begin(GLenum type) = 0;
92 virtual void vertex(int index) = 0;
93 virtual void edgeFlag(bool flag) = 0;
94 virtual void end() = 0;
95 virtual int combine(GLdouble coords[3], int vertexIndices[4],
96 GLfloat weight[4]) = 0;
97 static void beginCB(GLenum type, void* data) {
98 static_cast<GrTess*>(data)->begin(type);
99 }
100 static void vertexCB(void* vertexData, void* data) {
101 static_cast<GrTess*>(data)->vertex(reinterpret_cast<long>(vertexData));
102 }
103 static void edgeFlagCB(GLboolean flag, void* data) {
104 static_cast<GrTess*>(data)->edgeFlag(flag != 0);
105 }
106 static void endCB(void* data) {
107 static_cast<GrTess*>(data)->end();
108 }
109 static void combineCB(GLdouble coords[3], void* vertexData[4],
110 GLfloat weight[4], void **outData, void* data) {
111 int vertexIndex[4];
112 vertexIndex[0] = reinterpret_cast<long>(vertexData[0]);
113 vertexIndex[1] = reinterpret_cast<long>(vertexData[1]);
114 vertexIndex[2] = reinterpret_cast<long>(vertexData[2]);
115 vertexIndex[3] = reinterpret_cast<long>(vertexData[3]);
116 GrTess* tess = static_cast<GrTess*>(data);
117 int outIndex = tess->combine(coords, vertexIndex, weight);
118 *reinterpret_cast<long*>(outData) = outIndex;
119 }
120protected:
121 GLUtesselator* fTess;
122 GrPointArray fVertices;
123 double* fInVertices;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000124};
125
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000126class GrPolygonTess : public GrTess {
127public:
128 GrPolygonTess(int count, unsigned winding_rule)
129 : GrTess(count, winding_rule) {
130 }
131 ~GrPolygonTess() {
132 }
133 const GrIndexArray& indices() const { return fIndices; }
134protected:
135 virtual void begin(GLenum type) {
136 GR_DEBUGASSERT(type == GL_TRIANGLES);
137 }
138 virtual void vertex(int index) {
139 *fIndices.append() = index;
140 }
141 virtual void edgeFlag(bool flag) {}
142 virtual void end() {}
143 virtual int combine(GLdouble coords[3], int vertexIndices[4],
144 GLfloat weight[4]) {
145 int index = fVertices.count();
146 GrPoint p = GrPoint::Make(static_cast<float>(coords[0]),
147 static_cast<float>(coords[1]));
148 *fVertices.append() = p;
149 return index;
150 }
151protected:
152 GrIndexArray fIndices;
153};
154
155class GrEdgePolygonTess : public GrPolygonTess {
156public:
157 GrEdgePolygonTess(int count, unsigned winding_rule, const SkMatrix& matrix)
158 : GrPolygonTess(count, winding_rule),
159 fMatrix(matrix),
160 fEdgeFlag(false),
161 fEdgeVertex(-1),
162 fTriStartVertex(-1),
163 fEdges(NULL) {
164 }
165 ~GrEdgePolygonTess() {
166 delete[] fEdges;
167 }
168 const GrDrawTarget::Edge* edges() const { return fEdges; }
169private:
170 void addEdge(int index0, int index1) {
171 GrPoint p = fVertices[index0];
172 GrPoint q = fVertices[index1];
173 fMatrix.mapPoints(&p, 1);
174 fMatrix.mapPoints(&q, 1);
175 p = sanitizePoint(p);
176 q = sanitizePoint(q);
177 if (p == q) return;
178 GrDrawTarget::Edge edge = computeEdge(p, q, 1.0f);
179 fEdges[index0 * 2 + 1] = edge;
180 fEdges[index1 * 2] = edge;
181 }
182 virtual void begin(GLenum type) {
183 GR_DEBUGASSERT(type == GL_TRIANGLES);
184 int count = fVertices.count() * 2;
185 fEdges = new GrDrawTarget::Edge[count];
186 memset(fEdges, 0, count * sizeof(GrDrawTarget::Edge));
187 }
188 virtual void edgeFlag(bool flag) {
189 fEdgeFlag = flag;
190 }
191 virtual void vertex(int index) {
192 bool triStart = fIndices.count() % 3 == 0;
193 GrPolygonTess::vertex(index);
194 if (fEdgeVertex != -1) {
195 if (triStart) {
196 addEdge(fEdgeVertex, fTriStartVertex);
197 } else {
198 addEdge(fEdgeVertex, index);
199 }
200 }
201 if (triStart) {
202 fTriStartVertex = index;
203 }
204 if (fEdgeFlag) {
205 fEdgeVertex = index;
206 } else {
207 fEdgeVertex = -1;
208 }
209 }
210 virtual void end() {
211 if (fEdgeVertex != -1) {
212 addEdge(fEdgeVertex, fTriStartVertex);
213 }
214 }
215 GrMatrix fMatrix;
216 bool fEdgeFlag;
217 int fEdgeVertex, fTriStartVertex;
218 GrDrawTarget::Edge* fEdges;
219};
220
221class GrBoundaryTess : public GrTess {
222public:
223 GrBoundaryTess(int count, unsigned winding_rule)
224 : GrTess(count, winding_rule),
225 fContourStart(0) {
226 Sk_gluTessProperty(fTess, GLU_TESS_BOUNDARY_ONLY, 1);
227 }
228 ~GrBoundaryTess() {
229 }
230 GrPointArray& contourPoints() { return fContourPoints; }
231 const GrIndexArray& contours() const { return fContours; }
232private:
233 virtual void begin(GLenum type) {
234 fContourStart = fContourPoints.count();
235 }
236 virtual void vertex(int index) {
237 *fContourPoints.append() = fVertices.at(index);
238 }
239 virtual void edgeFlag(bool flag) {}
240 virtual void end() {
241 *fContours.append() = fContourPoints.count() - fContourStart;
242 }
243 virtual int combine(GLdouble coords[3], int vertexIndices[4],
244 GLfloat weight[4]) {
245 int index = fVertices.count();
246 *fVertices.append() = GrPoint::Make(static_cast<float>(coords[0]),
247 static_cast<float>(coords[1]));
248 return index;
249 }
250 GrPointArray fContourPoints;
251 GrIndexArray fContours;
252 size_t fContourStart;
253};
254
255static bool nearlyEqual(float a, float b) {
256 return fabsf(a - b) < 0.0001f;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000257}
258
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000259static bool nearlyEqual(const GrPoint& a, const GrPoint& b) {
260 return nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000261}
262
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000263static bool parallel(const GrDrawTarget::Edge& a, const GrDrawTarget::Edge& b) {
264 return (nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY)) ||
265 (nearlyEqual(a.fX, -b.fX) && nearlyEqual(a.fY, -b.fY));
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000266}
267
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000268static unsigned fill_type_to_glu_winding_rule(GrPathFill fill) {
269 switch (fill) {
270 case kWinding_PathFill:
271 return GLU_TESS_WINDING_NONZERO;
272 case kEvenOdd_PathFill:
273 return GLU_TESS_WINDING_ODD;
274 case kInverseWinding_PathFill:
275 return GLU_TESS_WINDING_POSITIVE;
276 case kInverseEvenOdd_PathFill:
277 return GLU_TESS_WINDING_ODD;
278 case kHairLine_PathFill:
279 return GLU_TESS_WINDING_NONZERO; // FIXME: handle this
280 default:
281 GrAssert(!"Unknown path fill!");
282 return 0;
283 }
284}
285
286GrTesselatedPathRenderer::GrTesselatedPathRenderer() {
287}
288
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000289static bool isCCW(const GrPoint* pts, int count) {
290 GrVec v1, v2;
291 do {
292 v1 = pts[1] - pts[0];
293 v2 = pts[2] - pts[1];
294 pts++;
295 count--;
296 } while (nearlyEqual(v1, v2) && count > 3);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000297 return v1.cross(v2) < 0;
298}
299
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000300static bool validEdge(const GrDrawTarget::Edge& edge) {
301 return !(edge.fX == 0.0f && edge.fY == 0.0f && edge.fZ == 0.0f);
302}
303
304static size_t computeEdgesAndIntersect(const GrMatrix& matrix,
305 const GrMatrix& inverse,
306 GrPoint* vertices,
307 size_t numVertices,
308 GrEdgeArray* edges,
309 float sign) {
310 if (numVertices < 3) {
311 return 0;
312 }
senorblanco@chromium.orgff174b32011-05-16 16:59:57 +0000313 matrix.mapPoints(vertices, numVertices);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000314 if (sign == 0.0f) {
315 sign = isCCW(vertices, numVertices) ? -1.0f : 1.0f;
316 }
317 GrPoint p = sanitizePoint(vertices[numVertices - 1]);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000318 for (size_t i = 0; i < numVertices; ++i) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000319 GrPoint q = sanitizePoint(vertices[i]);
320 if (p == q) {
321 continue;
322 }
323 GrDrawTarget::Edge edge = computeEdge(p, q, sign);
324 edge.fZ += 0.5f; // Offset by half a pixel along the tangent.
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000325 *edges->append() = edge;
326 p = q;
327 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000328 int count = edges->count();
329 if (count == 0) {
330 return 0;
331 }
332 GrDrawTarget::Edge prev_edge = edges->at(0);
333 for (int i = 0; i < count; ++i) {
334 GrDrawTarget::Edge edge = edges->at(i < count - 1 ? i + 1 : 0);
335 if (parallel(edge, prev_edge)) {
336 // 3 points are collinear; offset by half the tangent instead
337 vertices[i].fX -= edge.fX * 0.5f;
338 vertices[i].fY -= edge.fY * 0.5f;
339 } else {
340 vertices[i] = prev_edge.intersect(edge);
341 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000342 inverse.mapPoints(&vertices[i], 1);
343 prev_edge = edge;
344 }
345 return edges->count();
346}
347
bsalomon@google.comee435122011-07-01 14:57:55 +0000348void GrTesselatedPathRenderer::drawPath(GrDrawTarget::StageBitfield stages) {
349 GrDrawTarget::AutoStateRestore asr(fTarget);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000350 // face culling doesn't make sense here
bsalomon@google.comee435122011-07-01 14:57:55 +0000351 GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000352
bsalomon@google.comee435122011-07-01 14:57:55 +0000353 GrMatrix viewM = fTarget->getViewMatrix();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000354 // In order to tesselate the path we get a bound on how much the matrix can
355 // stretch when mapping to screen coordinates.
356 GrScalar stretch = viewM.getMaxStretch();
357 bool useStretch = stretch > 0;
tomhudson@google.comd22b6e42011-06-24 15:53:40 +0000358 GrScalar tol = fCurveTolerance;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000359
360 if (!useStretch) {
361 // TODO: deal with perspective in some better way.
362 tol /= 10;
363 } else {
bungeman@google.com8c5753e2011-05-20 19:11:50 +0000364 tol = GrScalarDiv(tol, stretch);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000365 }
366 GrScalar tolSqd = GrMul(tol, tol);
367
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000368 int subpathCnt;
bsalomon@google.comee435122011-07-01 14:57:55 +0000369 int maxPts = GrPathUtils::worstCasePointCount(*fPath, &subpathCnt, tol);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000370
371 GrVertexLayout layout = 0;
372 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
373 if ((1 << s) & stages) {
374 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
375 }
376 }
377
bsalomon@google.comee435122011-07-01 14:57:55 +0000378 bool inverted = IsFillInverted(fFill);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000379 if (inverted) {
380 maxPts += 4;
381 subpathCnt++;
382 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000383 if (maxPts > USHRT_MAX) {
384 return;
385 }
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000386 SkAutoSTMalloc<8, GrPoint> baseMem(maxPts);
387 GrPoint* base = baseMem;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000388 GrPoint* vert = base;
389 GrPoint* subpathBase = base;
390
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000391 SkAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000392
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000393 GrPoint pts[4];
bsalomon@google.comee435122011-07-01 14:57:55 +0000394 SkPath::Iter iter(*fPath, false);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000395
396 bool first = true;
397 int subpath = 0;
398
399 for (;;) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000400 switch (iter.next(pts)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000401 case kMove_PathCmd:
402 if (!first) {
403 subpathVertCount[subpath] = vert-subpathBase;
404 subpathBase = vert;
405 ++subpath;
406 }
407 *vert = pts[0];
408 vert++;
409 break;
410 case kLine_PathCmd:
411 *vert = pts[1];
412 vert++;
413 break;
414 case kQuadratic_PathCmd: {
415 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
416 tolSqd, &vert,
417 GrPathUtils::quadraticPointCount(pts, tol));
418 break;
419 }
420 case kCubic_PathCmd: {
421 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
422 tolSqd, &vert,
423 GrPathUtils::cubicPointCount(pts, tol));
424 break;
425 }
426 case kClose_PathCmd:
427 break;
428 case kEnd_PathCmd:
429 subpathVertCount[subpath] = vert-subpathBase;
430 ++subpath; // this could be only in debug
431 goto FINISHED;
432 }
433 first = false;
434 }
435FINISHED:
bsalomon@google.comee435122011-07-01 14:57:55 +0000436 if (0 != fTranslate.fX || 0 != fTranslate.fY) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000437 for (int i = 0; i < vert - base; i++) {
bsalomon@google.comee435122011-07-01 14:57:55 +0000438 base[i].offset(fTranslate.fX, fTranslate.fY);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000439 }
440 }
441
442 if (inverted) {
443 GrRect bounds;
bsalomon@google.comee435122011-07-01 14:57:55 +0000444 GrAssert(NULL != fTarget->getRenderTarget());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000445 bounds.setLTRB(0, 0,
bsalomon@google.comee435122011-07-01 14:57:55 +0000446 GrIntToScalar(fTarget->getRenderTarget()->width()),
447 GrIntToScalar(fTarget->getRenderTarget()->height()));
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000448 GrMatrix vmi;
bsalomon@google.comee435122011-07-01 14:57:55 +0000449 if (fTarget->getViewInverse(&vmi)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000450 vmi.mapRect(&bounds);
451 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000452 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
453 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
454 *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
455 *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000456 subpathVertCount[subpath++] = 4;
457 }
458
459 GrAssert(subpath == subpathCnt);
460 GrAssert((vert - base) <= maxPts);
461
462 size_t count = vert - base;
463
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000464 if (count < 3) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000465 return;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000466 }
467
bsalomon@google.comee435122011-07-01 14:57:55 +0000468 if (subpathCnt == 1 && !inverted && fPath->isConvex()) {
469 if (fTarget->isAntialiasState()) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000470 GrEdgeArray edges;
bsalomon@google.comee435122011-07-01 14:57:55 +0000471 GrMatrix inverse, matrix = fTarget->getViewMatrix();
472 fTarget->getViewInverse(&inverse);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000473
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000474 count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f);
bsalomon@google.comee435122011-07-01 14:57:55 +0000475 size_t maxEdges = fTarget->getMaxEdges();
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000476 if (count == 0) {
477 return;
478 }
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000479 if (count <= maxEdges) {
480 // All edges fit; upload all edges and draw all verts as a fan
bsalomon@google.comee435122011-07-01 14:57:55 +0000481 fTarget->setVertexSourceToArray(layout, base, count);
482 fTarget->setEdgeAAData(&edges[0], count);
483 fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000484 } else {
485 // Upload "maxEdges" edges and verts at a time, and draw as
486 // separate fans
487 for (size_t i = 0; i < count - 2; i += maxEdges - 2) {
488 edges[i] = edges[0];
489 base[i] = base[0];
490 int size = GR_CT_MIN(count - i, maxEdges);
bsalomon@google.comee435122011-07-01 14:57:55 +0000491 fTarget->setVertexSourceToArray(layout, &base[i], size);
492 fTarget->setEdgeAAData(&edges[i], size);
493 fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000494 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000495 }
bsalomon@google.comee435122011-07-01 14:57:55 +0000496 fTarget->setEdgeAAData(NULL, 0);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000497 } else {
bsalomon@google.comee435122011-07-01 14:57:55 +0000498 fTarget->setVertexSourceToArray(layout, base, count);
499 fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000500 }
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000501 return;
502 }
503
bsalomon@google.comee435122011-07-01 14:57:55 +0000504 if (fTarget->isAntialiasState()) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000505 // Run the tesselator once to get the boundaries.
bsalomon@google.comee435122011-07-01 14:57:55 +0000506 GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fFill));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000507 btess.addVertices(base, subpathVertCount, subpathCnt);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000508
bsalomon@google.comee435122011-07-01 14:57:55 +0000509 GrMatrix inverse, matrix = fTarget->getViewMatrix();
510 if (!fTarget->getViewInverse(&inverse)) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000511 return;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000512 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000513
514 if (btess.vertices().count() > USHRT_MAX) {
515 return;
516 }
517
518 // Inflate the boundary, and run the tesselator again to generate
519 // interior polys.
520 const GrPointArray& contourPoints = btess.contourPoints();
521 const GrIndexArray& contours = btess.contours();
522 GrEdgePolygonTess ptess(contourPoints.count(), GLU_TESS_WINDING_NONZERO, matrix);
523
524 size_t i = 0;
525 Sk_gluTessBeginPolygon(ptess.tess(), &ptess);
526 for (int contour = 0; contour < contours.count(); ++contour) {
527 int count = contours[contour];
528 GrEdgeArray edges;
529 int newCount = computeEdgesAndIntersect(matrix, inverse, &btess.contourPoints()[i], count, &edges, 1.0f);
530 Sk_gluTessBeginContour(ptess.tess());
531 for (int j = 0; j < newCount; j++) {
532 ptess.addVertex(contourPoints[i + j], ptess.vertices().count());
533 }
534 i += count;
535 Sk_gluTessEndContour(ptess.tess());
536 }
537
538 Sk_gluTessEndPolygon(ptess.tess());
539
540 if (ptess.vertices().count() > USHRT_MAX) {
541 return;
542 }
543
544 // Draw the resulting polys and upload their edge data.
bsalomon@google.comee435122011-07-01 14:57:55 +0000545 fTarget->enableState(GrDrawTarget::kEdgeAAConcave_StateBit);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000546 const GrPointArray& vertices = ptess.vertices();
547 const GrIndexArray& indices = ptess.indices();
548 const GrDrawTarget::Edge* edges = ptess.edges();
549 GR_DEBUGASSERT(indices.count() % 3 == 0);
550 for (int i = 0; i < indices.count(); i += 3) {
551 GrPoint tri_verts[3];
552 int index0 = indices[i];
553 int index1 = indices[i + 1];
554 int index2 = indices[i + 2];
555 tri_verts[0] = vertices[index0];
556 tri_verts[1] = vertices[index1];
557 tri_verts[2] = vertices[index2];
558 GrDrawTarget::Edge tri_edges[6];
559 int t = 0;
560 const GrDrawTarget::Edge& edge0 = edges[index0 * 2];
561 const GrDrawTarget::Edge& edge1 = edges[index0 * 2 + 1];
562 const GrDrawTarget::Edge& edge2 = edges[index1 * 2];
563 const GrDrawTarget::Edge& edge3 = edges[index1 * 2 + 1];
564 const GrDrawTarget::Edge& edge4 = edges[index2 * 2];
565 const GrDrawTarget::Edge& edge5 = edges[index2 * 2 + 1];
566 if (validEdge(edge0) && validEdge(edge1)) {
567 tri_edges[t++] = edge0;
568 tri_edges[t++] = edge1;
569 }
570 if (validEdge(edge2) && validEdge(edge3)) {
571 tri_edges[t++] = edge2;
572 tri_edges[t++] = edge3;
573 }
574 if (validEdge(edge4) && validEdge(edge5)) {
575 tri_edges[t++] = edge4;
576 tri_edges[t++] = edge5;
577 }
bsalomon@google.comee435122011-07-01 14:57:55 +0000578 fTarget->setEdgeAAData(&tri_edges[0], t);
579 fTarget->setVertexSourceToArray(layout, &tri_verts[0], 3);
580 fTarget->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000581 }
bsalomon@google.comee435122011-07-01 14:57:55 +0000582 fTarget->setEdgeAAData(NULL, 0);
583 fTarget->disableState(GrDrawTarget::kEdgeAAConcave_StateBit);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000584 return;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000585 }
586
bsalomon@google.comee435122011-07-01 14:57:55 +0000587 GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fFill));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000588 ptess.addVertices(base, subpathVertCount, subpathCnt);
589 const GrPointArray& vertices = ptess.vertices();
590 const GrIndexArray& indices = ptess.indices();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000591 if (indices.count() > 0) {
bsalomon@google.comee435122011-07-01 14:57:55 +0000592 fTarget->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
593 fTarget->setIndexSourceToArray(indices.begin(), indices.count());
594 fTarget->drawIndexed(kTriangles_PrimitiveType,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000595 0,
596 0,
597 vertices.count(),
598 indices.count());
599 }
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000600}
601
bsalomon@google.comee435122011-07-01 14:57:55 +0000602bool GrTesselatedPathRenderer::canDrawPath(const SkPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000603 GrPathFill fill) const {
604 return kHairLine_PathFill != fill;
605}
606
bsalomon@google.comee435122011-07-01 14:57:55 +0000607void GrTesselatedPathRenderer::drawPathToStencil() {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000608 GrAlwaysAssert(!"multipass stencil should not be needed");
609}
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000610
611bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000612 const SkPath& path,
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000613 GrPathFill fill) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000614 return true;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000615}