blob: 5ddba99e387748e21fe40cbe86e329f3dba9f66a [file] [log] [blame]
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +00001/*
2 Copyright 2011 Google Inc.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17#include "GrTesselatedPathRenderer.h"
18
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000019#include "GrPathUtils.h"
reed@google.com07f3ee12011-05-16 17:21:57 +000020#include "GrPoint.h"
21#include "GrTDArray.h"
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000022
bsalomon@google.com3582bf92011-06-30 21:32:31 +000023#include "SkTemplates.h"
24
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000025#include <limits.h>
senorblanco@chromium.org1fa803d2011-05-25 14:46:17 +000026#include <sk_glu.h>
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000027
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000028typedef GrTDArray<GrDrawTarget::Edge> GrEdgeArray;
29typedef GrTDArray<GrPoint> GrPointArray;
30typedef GrTDArray<uint16_t> GrIndexArray;
31typedef void (*TESSCB)();
32
33// limit the allowable vertex range to approximately half of the representable
34// IEEE exponent in order to avoid overflow when doing multiplies between
35// vertex components,
36const float kMaxVertexValue = 1e18;
37
38static inline GrDrawTarget::Edge computeEdge(const GrPoint& p,
39 const GrPoint& q,
40 float sign) {
41 GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
42 float scale = sign / tangent.length();
43 float cross2 = p.fX * q.fY - q.fX * p.fY;
44 return GrDrawTarget::Edge(tangent.fX * scale,
45 tangent.fY * scale,
46 cross2 * scale);
47}
48
49static inline GrPoint sanitizePoint(const GrPoint& pt) {
50 GrPoint r;
51 r.fX = SkScalarPin(pt.fX, -kMaxVertexValue, kMaxVertexValue);
52 r.fY = SkScalarPin(pt.fY, -kMaxVertexValue, kMaxVertexValue);
53 return r;
54}
55
56class GrTess {
57public:
58 GrTess(int count, unsigned winding_rule) {
59 fTess = Sk_gluNewTess();
60 Sk_gluTessProperty(fTess, GLU_TESS_WINDING_RULE, winding_rule);
61 Sk_gluTessNormal(fTess, 0.0f, 0.0f, 1.0f);
62 Sk_gluTessCallback(fTess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginCB);
63 Sk_gluTessCallback(fTess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexCB);
64 Sk_gluTessCallback(fTess, GLU_TESS_END_DATA, (TESSCB) &endCB);
65 Sk_gluTessCallback(fTess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagCB);
66 Sk_gluTessCallback(fTess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineCB);
67 fInVertices = new double[count * 3];
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000068 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000069 ~GrTess() {
70 Sk_gluDeleteTess(fTess);
71 delete[] fInVertices;
72 }
73 void addVertex(const GrPoint& pt, int index) {
74 if (index > USHRT_MAX) return;
75 double* inVertex = &fInVertices[index * 3];
76 inVertex[0] = pt.fX;
77 inVertex[1] = pt.fY;
78 inVertex[2] = 0.0;
79 *fVertices.append() = pt;
80 Sk_gluTessVertex(fTess, inVertex, reinterpret_cast<void*>(index));
81 }
82 void addVertices(const GrPoint* points, const uint16_t* contours, int numContours) {
83 Sk_gluTessBeginPolygon(fTess, this);
84 size_t i = 0;
85 for (int j = 0; j < numContours; ++j) {
86 Sk_gluTessBeginContour(fTess);
87 size_t end = i + contours[j];
88 for (; i < end; ++i) {
89 addVertex(points[i], i);
90 }
91 Sk_gluTessEndContour(fTess);
92 }
93 Sk_gluTessEndPolygon(fTess);
94 }
95 GLUtesselator* tess() { return fTess; }
96 const GrPointArray& vertices() const { return fVertices; }
97protected:
98 virtual void begin(GLenum type) = 0;
99 virtual void vertex(int index) = 0;
100 virtual void edgeFlag(bool flag) = 0;
101 virtual void end() = 0;
102 virtual int combine(GLdouble coords[3], int vertexIndices[4],
103 GLfloat weight[4]) = 0;
104 static void beginCB(GLenum type, void* data) {
105 static_cast<GrTess*>(data)->begin(type);
106 }
107 static void vertexCB(void* vertexData, void* data) {
108 static_cast<GrTess*>(data)->vertex(reinterpret_cast<long>(vertexData));
109 }
110 static void edgeFlagCB(GLboolean flag, void* data) {
111 static_cast<GrTess*>(data)->edgeFlag(flag != 0);
112 }
113 static void endCB(void* data) {
114 static_cast<GrTess*>(data)->end();
115 }
116 static void combineCB(GLdouble coords[3], void* vertexData[4],
117 GLfloat weight[4], void **outData, void* data) {
118 int vertexIndex[4];
119 vertexIndex[0] = reinterpret_cast<long>(vertexData[0]);
120 vertexIndex[1] = reinterpret_cast<long>(vertexData[1]);
121 vertexIndex[2] = reinterpret_cast<long>(vertexData[2]);
122 vertexIndex[3] = reinterpret_cast<long>(vertexData[3]);
123 GrTess* tess = static_cast<GrTess*>(data);
124 int outIndex = tess->combine(coords, vertexIndex, weight);
125 *reinterpret_cast<long*>(outData) = outIndex;
126 }
127protected:
128 GLUtesselator* fTess;
129 GrPointArray fVertices;
130 double* fInVertices;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000131};
132
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000133class GrPolygonTess : public GrTess {
134public:
135 GrPolygonTess(int count, unsigned winding_rule)
136 : GrTess(count, winding_rule) {
137 }
138 ~GrPolygonTess() {
139 }
140 const GrIndexArray& indices() const { return fIndices; }
141protected:
142 virtual void begin(GLenum type) {
143 GR_DEBUGASSERT(type == GL_TRIANGLES);
144 }
145 virtual void vertex(int index) {
146 *fIndices.append() = index;
147 }
148 virtual void edgeFlag(bool flag) {}
149 virtual void end() {}
150 virtual int combine(GLdouble coords[3], int vertexIndices[4],
151 GLfloat weight[4]) {
152 int index = fVertices.count();
153 GrPoint p = GrPoint::Make(static_cast<float>(coords[0]),
154 static_cast<float>(coords[1]));
155 *fVertices.append() = p;
156 return index;
157 }
158protected:
159 GrIndexArray fIndices;
160};
161
162class GrEdgePolygonTess : public GrPolygonTess {
163public:
164 GrEdgePolygonTess(int count, unsigned winding_rule, const SkMatrix& matrix)
165 : GrPolygonTess(count, winding_rule),
166 fMatrix(matrix),
167 fEdgeFlag(false),
168 fEdgeVertex(-1),
169 fTriStartVertex(-1),
170 fEdges(NULL) {
171 }
172 ~GrEdgePolygonTess() {
173 delete[] fEdges;
174 }
175 const GrDrawTarget::Edge* edges() const { return fEdges; }
176private:
177 void addEdge(int index0, int index1) {
178 GrPoint p = fVertices[index0];
179 GrPoint q = fVertices[index1];
180 fMatrix.mapPoints(&p, 1);
181 fMatrix.mapPoints(&q, 1);
182 p = sanitizePoint(p);
183 q = sanitizePoint(q);
184 if (p == q) return;
185 GrDrawTarget::Edge edge = computeEdge(p, q, 1.0f);
186 fEdges[index0 * 2 + 1] = edge;
187 fEdges[index1 * 2] = edge;
188 }
189 virtual void begin(GLenum type) {
190 GR_DEBUGASSERT(type == GL_TRIANGLES);
191 int count = fVertices.count() * 2;
192 fEdges = new GrDrawTarget::Edge[count];
193 memset(fEdges, 0, count * sizeof(GrDrawTarget::Edge));
194 }
195 virtual void edgeFlag(bool flag) {
196 fEdgeFlag = flag;
197 }
198 virtual void vertex(int index) {
199 bool triStart = fIndices.count() % 3 == 0;
200 GrPolygonTess::vertex(index);
201 if (fEdgeVertex != -1) {
202 if (triStart) {
203 addEdge(fEdgeVertex, fTriStartVertex);
204 } else {
205 addEdge(fEdgeVertex, index);
206 }
207 }
208 if (triStart) {
209 fTriStartVertex = index;
210 }
211 if (fEdgeFlag) {
212 fEdgeVertex = index;
213 } else {
214 fEdgeVertex = -1;
215 }
216 }
217 virtual void end() {
218 if (fEdgeVertex != -1) {
219 addEdge(fEdgeVertex, fTriStartVertex);
220 }
221 }
222 GrMatrix fMatrix;
223 bool fEdgeFlag;
224 int fEdgeVertex, fTriStartVertex;
225 GrDrawTarget::Edge* fEdges;
226};
227
228class GrBoundaryTess : public GrTess {
229public:
230 GrBoundaryTess(int count, unsigned winding_rule)
231 : GrTess(count, winding_rule),
232 fContourStart(0) {
233 Sk_gluTessProperty(fTess, GLU_TESS_BOUNDARY_ONLY, 1);
234 }
235 ~GrBoundaryTess() {
236 }
237 GrPointArray& contourPoints() { return fContourPoints; }
238 const GrIndexArray& contours() const { return fContours; }
239private:
240 virtual void begin(GLenum type) {
241 fContourStart = fContourPoints.count();
242 }
243 virtual void vertex(int index) {
244 *fContourPoints.append() = fVertices.at(index);
245 }
246 virtual void edgeFlag(bool flag) {}
247 virtual void end() {
248 *fContours.append() = fContourPoints.count() - fContourStart;
249 }
250 virtual int combine(GLdouble coords[3], int vertexIndices[4],
251 GLfloat weight[4]) {
252 int index = fVertices.count();
253 *fVertices.append() = GrPoint::Make(static_cast<float>(coords[0]),
254 static_cast<float>(coords[1]));
255 return index;
256 }
257 GrPointArray fContourPoints;
258 GrIndexArray fContours;
259 size_t fContourStart;
260};
261
262static bool nearlyEqual(float a, float b) {
263 return fabsf(a - b) < 0.0001f;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000264}
265
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000266static bool nearlyEqual(const GrPoint& a, const GrPoint& b) {
267 return nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000268}
269
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000270static bool parallel(const GrDrawTarget::Edge& a, const GrDrawTarget::Edge& b) {
271 return (nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY)) ||
272 (nearlyEqual(a.fX, -b.fX) && nearlyEqual(a.fY, -b.fY));
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000273}
274
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000275static unsigned fill_type_to_glu_winding_rule(GrPathFill fill) {
276 switch (fill) {
277 case kWinding_PathFill:
278 return GLU_TESS_WINDING_NONZERO;
279 case kEvenOdd_PathFill:
280 return GLU_TESS_WINDING_ODD;
281 case kInverseWinding_PathFill:
282 return GLU_TESS_WINDING_POSITIVE;
283 case kInverseEvenOdd_PathFill:
284 return GLU_TESS_WINDING_ODD;
285 case kHairLine_PathFill:
286 return GLU_TESS_WINDING_NONZERO; // FIXME: handle this
287 default:
288 GrAssert(!"Unknown path fill!");
289 return 0;
290 }
291}
292
293GrTesselatedPathRenderer::GrTesselatedPathRenderer() {
294}
295
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000296static bool isCCW(const GrPoint* pts, int count) {
297 GrVec v1, v2;
298 do {
299 v1 = pts[1] - pts[0];
300 v2 = pts[2] - pts[1];
301 pts++;
302 count--;
303 } while (nearlyEqual(v1, v2) && count > 3);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000304 return v1.cross(v2) < 0;
305}
306
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000307static bool validEdge(const GrDrawTarget::Edge& edge) {
308 return !(edge.fX == 0.0f && edge.fY == 0.0f && edge.fZ == 0.0f);
309}
310
311static size_t computeEdgesAndIntersect(const GrMatrix& matrix,
312 const GrMatrix& inverse,
313 GrPoint* vertices,
314 size_t numVertices,
315 GrEdgeArray* edges,
316 float sign) {
317 if (numVertices < 3) {
318 return 0;
319 }
senorblanco@chromium.orgff174b32011-05-16 16:59:57 +0000320 matrix.mapPoints(vertices, numVertices);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000321 if (sign == 0.0f) {
322 sign = isCCW(vertices, numVertices) ? -1.0f : 1.0f;
323 }
324 GrPoint p = sanitizePoint(vertices[numVertices - 1]);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000325 for (size_t i = 0; i < numVertices; ++i) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000326 GrPoint q = sanitizePoint(vertices[i]);
327 if (p == q) {
328 continue;
329 }
330 GrDrawTarget::Edge edge = computeEdge(p, q, sign);
331 edge.fZ += 0.5f; // Offset by half a pixel along the tangent.
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000332 *edges->append() = edge;
333 p = q;
334 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000335 int count = edges->count();
336 if (count == 0) {
337 return 0;
338 }
339 GrDrawTarget::Edge prev_edge = edges->at(0);
340 for (int i = 0; i < count; ++i) {
341 GrDrawTarget::Edge edge = edges->at(i < count - 1 ? i + 1 : 0);
342 if (parallel(edge, prev_edge)) {
343 // 3 points are collinear; offset by half the tangent instead
344 vertices[i].fX -= edge.fX * 0.5f;
345 vertices[i].fY -= edge.fY * 0.5f;
346 } else {
347 vertices[i] = prev_edge.intersect(edge);
348 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000349 inverse.mapPoints(&vertices[i], 1);
350 prev_edge = edge;
351 }
352 return edges->count();
353}
354
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000355void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
356 GrDrawTarget::StageBitfield stages,
reed@google.com07f3ee12011-05-16 17:21:57 +0000357 const GrPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000358 GrPathFill fill,
359 const GrPoint* translate) {
360 GrDrawTarget::AutoStateRestore asr(target);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000361 // face culling doesn't make sense here
362 GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
363
364 GrMatrix viewM = target->getViewMatrix();
365 // In order to tesselate the path we get a bound on how much the matrix can
366 // stretch when mapping to screen coordinates.
367 GrScalar stretch = viewM.getMaxStretch();
368 bool useStretch = stretch > 0;
tomhudson@google.comd22b6e42011-06-24 15:53:40 +0000369 GrScalar tol = fCurveTolerance;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000370
371 if (!useStretch) {
372 // TODO: deal with perspective in some better way.
373 tol /= 10;
374 } else {
bungeman@google.com8c5753e2011-05-20 19:11:50 +0000375 tol = GrScalarDiv(tol, stretch);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000376 }
377 GrScalar tolSqd = GrMul(tol, tol);
378
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000379 int subpathCnt;
380 int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
381
382 GrVertexLayout layout = 0;
383 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
384 if ((1 << s) & stages) {
385 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
386 }
387 }
388
389 bool inverted = IsFillInverted(fill);
390 if (inverted) {
391 maxPts += 4;
392 subpathCnt++;
393 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000394 if (maxPts > USHRT_MAX) {
395 return;
396 }
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000397 SkAutoSTMalloc<8, GrPoint> baseMem(maxPts);
398 GrPoint* base = baseMem;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000399 GrPoint* vert = base;
400 GrPoint* subpathBase = base;
401
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000402 SkAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000403
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000404 GrPoint pts[4];
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000405 SkPath::Iter iter(path, false);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000406
407 bool first = true;
408 int subpath = 0;
409
410 for (;;) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000411 switch (iter.next(pts)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000412 case kMove_PathCmd:
413 if (!first) {
414 subpathVertCount[subpath] = vert-subpathBase;
415 subpathBase = vert;
416 ++subpath;
417 }
418 *vert = pts[0];
419 vert++;
420 break;
421 case kLine_PathCmd:
422 *vert = pts[1];
423 vert++;
424 break;
425 case kQuadratic_PathCmd: {
426 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
427 tolSqd, &vert,
428 GrPathUtils::quadraticPointCount(pts, tol));
429 break;
430 }
431 case kCubic_PathCmd: {
432 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
433 tolSqd, &vert,
434 GrPathUtils::cubicPointCount(pts, tol));
435 break;
436 }
437 case kClose_PathCmd:
438 break;
439 case kEnd_PathCmd:
440 subpathVertCount[subpath] = vert-subpathBase;
441 ++subpath; // this could be only in debug
442 goto FINISHED;
443 }
444 first = false;
445 }
446FINISHED:
447 if (translate) {
448 for (int i = 0; i < vert - base; i++) {
449 base[i].offset(translate->fX, translate->fY);
450 }
451 }
452
453 if (inverted) {
454 GrRect bounds;
455 GrAssert(NULL != target->getRenderTarget());
456 bounds.setLTRB(0, 0,
457 GrIntToScalar(target->getRenderTarget()->width()),
458 GrIntToScalar(target->getRenderTarget()->height()));
459 GrMatrix vmi;
460 if (target->getViewInverse(&vmi)) {
461 vmi.mapRect(&bounds);
462 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000463 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
464 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
465 *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
466 *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000467 subpathVertCount[subpath++] = 4;
468 }
469
470 GrAssert(subpath == subpathCnt);
471 GrAssert((vert - base) <= maxPts);
472
473 size_t count = vert - base;
474
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000475 if (count < 3) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000476 return;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000477 }
478
reed@google.com07f3ee12011-05-16 17:21:57 +0000479 if (subpathCnt == 1 && !inverted && path.isConvex()) {
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000480 if (target->isAntialiasState()) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000481 GrEdgeArray edges;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000482 GrMatrix inverse, matrix = target->getViewMatrix();
483 target->getViewInverse(&inverse);
484
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000485 count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f);
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000486 size_t maxEdges = target->getMaxEdges();
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000487 if (count == 0) {
488 return;
489 }
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000490 if (count <= maxEdges) {
491 // All edges fit; upload all edges and draw all verts as a fan
492 target->setVertexSourceToArray(layout, base, count);
493 target->setEdgeAAData(&edges[0], count);
494 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
495 } else {
496 // Upload "maxEdges" edges and verts at a time, and draw as
497 // separate fans
498 for (size_t i = 0; i < count - 2; i += maxEdges - 2) {
499 edges[i] = edges[0];
500 base[i] = base[0];
501 int size = GR_CT_MIN(count - i, maxEdges);
502 target->setVertexSourceToArray(layout, &base[i], size);
503 target->setEdgeAAData(&edges[i], size);
504 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
505 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000506 }
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000507 target->setEdgeAAData(NULL, 0);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000508 } else {
509 target->setVertexSourceToArray(layout, base, count);
510 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
511 }
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000512 return;
513 }
514
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000515 if (target->isAntialiasState()) {
516 // Run the tesselator once to get the boundaries.
517 GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fill));
518 btess.addVertices(base, subpathVertCount, subpathCnt);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000519
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000520 GrMatrix inverse, matrix = target->getViewMatrix();
521 if (!target->getViewInverse(&inverse)) {
522 return;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000523 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000524
525 if (btess.vertices().count() > USHRT_MAX) {
526 return;
527 }
528
529 // Inflate the boundary, and run the tesselator again to generate
530 // interior polys.
531 const GrPointArray& contourPoints = btess.contourPoints();
532 const GrIndexArray& contours = btess.contours();
533 GrEdgePolygonTess ptess(contourPoints.count(), GLU_TESS_WINDING_NONZERO, matrix);
534
535 size_t i = 0;
536 Sk_gluTessBeginPolygon(ptess.tess(), &ptess);
537 for (int contour = 0; contour < contours.count(); ++contour) {
538 int count = contours[contour];
539 GrEdgeArray edges;
540 int newCount = computeEdgesAndIntersect(matrix, inverse, &btess.contourPoints()[i], count, &edges, 1.0f);
541 Sk_gluTessBeginContour(ptess.tess());
542 for (int j = 0; j < newCount; j++) {
543 ptess.addVertex(contourPoints[i + j], ptess.vertices().count());
544 }
545 i += count;
546 Sk_gluTessEndContour(ptess.tess());
547 }
548
549 Sk_gluTessEndPolygon(ptess.tess());
550
551 if (ptess.vertices().count() > USHRT_MAX) {
552 return;
553 }
554
555 // Draw the resulting polys and upload their edge data.
556 target->enableState(GrDrawTarget::kEdgeAAConcave_StateBit);
557 const GrPointArray& vertices = ptess.vertices();
558 const GrIndexArray& indices = ptess.indices();
559 const GrDrawTarget::Edge* edges = ptess.edges();
560 GR_DEBUGASSERT(indices.count() % 3 == 0);
561 for (int i = 0; i < indices.count(); i += 3) {
562 GrPoint tri_verts[3];
563 int index0 = indices[i];
564 int index1 = indices[i + 1];
565 int index2 = indices[i + 2];
566 tri_verts[0] = vertices[index0];
567 tri_verts[1] = vertices[index1];
568 tri_verts[2] = vertices[index2];
569 GrDrawTarget::Edge tri_edges[6];
570 int t = 0;
571 const GrDrawTarget::Edge& edge0 = edges[index0 * 2];
572 const GrDrawTarget::Edge& edge1 = edges[index0 * 2 + 1];
573 const GrDrawTarget::Edge& edge2 = edges[index1 * 2];
574 const GrDrawTarget::Edge& edge3 = edges[index1 * 2 + 1];
575 const GrDrawTarget::Edge& edge4 = edges[index2 * 2];
576 const GrDrawTarget::Edge& edge5 = edges[index2 * 2 + 1];
577 if (validEdge(edge0) && validEdge(edge1)) {
578 tri_edges[t++] = edge0;
579 tri_edges[t++] = edge1;
580 }
581 if (validEdge(edge2) && validEdge(edge3)) {
582 tri_edges[t++] = edge2;
583 tri_edges[t++] = edge3;
584 }
585 if (validEdge(edge4) && validEdge(edge5)) {
586 tri_edges[t++] = edge4;
587 tri_edges[t++] = edge5;
588 }
589 target->setEdgeAAData(&tri_edges[0], t);
590 target->setVertexSourceToArray(layout, &tri_verts[0], 3);
591 target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
592 }
593 target->setEdgeAAData(NULL, 0);
594 target->disableState(GrDrawTarget::kEdgeAAConcave_StateBit);
595 return;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000596 }
597
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000598 GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fill));
599 ptess.addVertices(base, subpathVertCount, subpathCnt);
600 const GrPointArray& vertices = ptess.vertices();
601 const GrIndexArray& indices = ptess.indices();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000602 if (indices.count() > 0) {
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000603 target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
604 target->setIndexSourceToArray(indices.begin(), indices.count());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000605 target->drawIndexed(kTriangles_PrimitiveType,
606 0,
607 0,
608 vertices.count(),
609 indices.count());
610 }
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000611}
612
613bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000614 const SkPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000615 GrPathFill fill) const {
616 return kHairLine_PathFill != fill;
617}
618
619void GrTesselatedPathRenderer::drawPathToStencil(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000620 const SkPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000621 GrPathFill fill,
622 const GrPoint* translate) {
623 GrAlwaysAssert(!"multipass stencil should not be needed");
624}
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000625
626bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000627 const SkPath& path,
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000628 GrPathFill fill) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000629 return true;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000630}