blob: c85040a94195c77ae18826857b46f3895857002f [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,
bsalomon@google.comee435122011-07-01 14:57:55 +000036const float kMaxVertexValue = 1e18f;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000037
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
bsalomon@google.comee435122011-07-01 14:57:55 +0000355void GrTesselatedPathRenderer::drawPath(GrDrawTarget::StageBitfield stages) {
356 GrDrawTarget::AutoStateRestore asr(fTarget);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000357 // face culling doesn't make sense here
bsalomon@google.comee435122011-07-01 14:57:55 +0000358 GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000359
bsalomon@google.comee435122011-07-01 14:57:55 +0000360 GrMatrix viewM = fTarget->getViewMatrix();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000361 // In order to tesselate the path we get a bound on how much the matrix can
362 // stretch when mapping to screen coordinates.
363 GrScalar stretch = viewM.getMaxStretch();
364 bool useStretch = stretch > 0;
tomhudson@google.comd22b6e42011-06-24 15:53:40 +0000365 GrScalar tol = fCurveTolerance;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000366
367 if (!useStretch) {
368 // TODO: deal with perspective in some better way.
369 tol /= 10;
370 } else {
bungeman@google.com8c5753e2011-05-20 19:11:50 +0000371 tol = GrScalarDiv(tol, stretch);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000372 }
373 GrScalar tolSqd = GrMul(tol, tol);
374
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000375 int subpathCnt;
bsalomon@google.comee435122011-07-01 14:57:55 +0000376 int maxPts = GrPathUtils::worstCasePointCount(*fPath, &subpathCnt, tol);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000377
378 GrVertexLayout layout = 0;
379 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
380 if ((1 << s) & stages) {
381 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
382 }
383 }
384
bsalomon@google.comee435122011-07-01 14:57:55 +0000385 bool inverted = IsFillInverted(fFill);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000386 if (inverted) {
387 maxPts += 4;
388 subpathCnt++;
389 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000390 if (maxPts > USHRT_MAX) {
391 return;
392 }
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000393 SkAutoSTMalloc<8, GrPoint> baseMem(maxPts);
394 GrPoint* base = baseMem;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000395 GrPoint* vert = base;
396 GrPoint* subpathBase = base;
397
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000398 SkAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000399
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000400 GrPoint pts[4];
bsalomon@google.comee435122011-07-01 14:57:55 +0000401 SkPath::Iter iter(*fPath, false);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000402
403 bool first = true;
404 int subpath = 0;
405
406 for (;;) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000407 switch (iter.next(pts)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000408 case kMove_PathCmd:
409 if (!first) {
410 subpathVertCount[subpath] = vert-subpathBase;
411 subpathBase = vert;
412 ++subpath;
413 }
414 *vert = pts[0];
415 vert++;
416 break;
417 case kLine_PathCmd:
418 *vert = pts[1];
419 vert++;
420 break;
421 case kQuadratic_PathCmd: {
422 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
423 tolSqd, &vert,
424 GrPathUtils::quadraticPointCount(pts, tol));
425 break;
426 }
427 case kCubic_PathCmd: {
428 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
429 tolSqd, &vert,
430 GrPathUtils::cubicPointCount(pts, tol));
431 break;
432 }
433 case kClose_PathCmd:
434 break;
435 case kEnd_PathCmd:
436 subpathVertCount[subpath] = vert-subpathBase;
437 ++subpath; // this could be only in debug
438 goto FINISHED;
439 }
440 first = false;
441 }
442FINISHED:
bsalomon@google.comee435122011-07-01 14:57:55 +0000443 if (0 != fTranslate.fX || 0 != fTranslate.fY) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000444 for (int i = 0; i < vert - base; i++) {
bsalomon@google.comee435122011-07-01 14:57:55 +0000445 base[i].offset(fTranslate.fX, fTranslate.fY);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000446 }
447 }
448
449 if (inverted) {
450 GrRect bounds;
bsalomon@google.comee435122011-07-01 14:57:55 +0000451 GrAssert(NULL != fTarget->getRenderTarget());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000452 bounds.setLTRB(0, 0,
bsalomon@google.comee435122011-07-01 14:57:55 +0000453 GrIntToScalar(fTarget->getRenderTarget()->width()),
454 GrIntToScalar(fTarget->getRenderTarget()->height()));
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000455 GrMatrix vmi;
bsalomon@google.comee435122011-07-01 14:57:55 +0000456 if (fTarget->getViewInverse(&vmi)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000457 vmi.mapRect(&bounds);
458 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000459 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
460 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
461 *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
462 *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000463 subpathVertCount[subpath++] = 4;
464 }
465
466 GrAssert(subpath == subpathCnt);
467 GrAssert((vert - base) <= maxPts);
468
469 size_t count = vert - base;
470
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000471 if (count < 3) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000472 return;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000473 }
474
bsalomon@google.comee435122011-07-01 14:57:55 +0000475 if (subpathCnt == 1 && !inverted && fPath->isConvex()) {
476 if (fTarget->isAntialiasState()) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000477 GrEdgeArray edges;
bsalomon@google.comee435122011-07-01 14:57:55 +0000478 GrMatrix inverse, matrix = fTarget->getViewMatrix();
479 fTarget->getViewInverse(&inverse);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000480
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000481 count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f);
bsalomon@google.comee435122011-07-01 14:57:55 +0000482 size_t maxEdges = fTarget->getMaxEdges();
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000483 if (count == 0) {
484 return;
485 }
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000486 if (count <= maxEdges) {
487 // All edges fit; upload all edges and draw all verts as a fan
bsalomon@google.comee435122011-07-01 14:57:55 +0000488 fTarget->setVertexSourceToArray(layout, base, count);
489 fTarget->setEdgeAAData(&edges[0], count);
490 fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000491 } else {
492 // Upload "maxEdges" edges and verts at a time, and draw as
493 // separate fans
494 for (size_t i = 0; i < count - 2; i += maxEdges - 2) {
495 edges[i] = edges[0];
496 base[i] = base[0];
497 int size = GR_CT_MIN(count - i, maxEdges);
bsalomon@google.comee435122011-07-01 14:57:55 +0000498 fTarget->setVertexSourceToArray(layout, &base[i], size);
499 fTarget->setEdgeAAData(&edges[i], size);
500 fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000501 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000502 }
bsalomon@google.comee435122011-07-01 14:57:55 +0000503 fTarget->setEdgeAAData(NULL, 0);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000504 } else {
bsalomon@google.comee435122011-07-01 14:57:55 +0000505 fTarget->setVertexSourceToArray(layout, base, count);
506 fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000507 }
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000508 return;
509 }
510
bsalomon@google.comee435122011-07-01 14:57:55 +0000511 if (fTarget->isAntialiasState()) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000512 // Run the tesselator once to get the boundaries.
bsalomon@google.comee435122011-07-01 14:57:55 +0000513 GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fFill));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000514 btess.addVertices(base, subpathVertCount, subpathCnt);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000515
bsalomon@google.comee435122011-07-01 14:57:55 +0000516 GrMatrix inverse, matrix = fTarget->getViewMatrix();
517 if (!fTarget->getViewInverse(&inverse)) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000518 return;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000519 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000520
521 if (btess.vertices().count() > USHRT_MAX) {
522 return;
523 }
524
525 // Inflate the boundary, and run the tesselator again to generate
526 // interior polys.
527 const GrPointArray& contourPoints = btess.contourPoints();
528 const GrIndexArray& contours = btess.contours();
529 GrEdgePolygonTess ptess(contourPoints.count(), GLU_TESS_WINDING_NONZERO, matrix);
530
531 size_t i = 0;
532 Sk_gluTessBeginPolygon(ptess.tess(), &ptess);
533 for (int contour = 0; contour < contours.count(); ++contour) {
534 int count = contours[contour];
535 GrEdgeArray edges;
536 int newCount = computeEdgesAndIntersect(matrix, inverse, &btess.contourPoints()[i], count, &edges, 1.0f);
537 Sk_gluTessBeginContour(ptess.tess());
538 for (int j = 0; j < newCount; j++) {
539 ptess.addVertex(contourPoints[i + j], ptess.vertices().count());
540 }
541 i += count;
542 Sk_gluTessEndContour(ptess.tess());
543 }
544
545 Sk_gluTessEndPolygon(ptess.tess());
546
547 if (ptess.vertices().count() > USHRT_MAX) {
548 return;
549 }
550
551 // Draw the resulting polys and upload their edge data.
bsalomon@google.comee435122011-07-01 14:57:55 +0000552 fTarget->enableState(GrDrawTarget::kEdgeAAConcave_StateBit);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000553 const GrPointArray& vertices = ptess.vertices();
554 const GrIndexArray& indices = ptess.indices();
555 const GrDrawTarget::Edge* edges = ptess.edges();
556 GR_DEBUGASSERT(indices.count() % 3 == 0);
557 for (int i = 0; i < indices.count(); i += 3) {
558 GrPoint tri_verts[3];
559 int index0 = indices[i];
560 int index1 = indices[i + 1];
561 int index2 = indices[i + 2];
562 tri_verts[0] = vertices[index0];
563 tri_verts[1] = vertices[index1];
564 tri_verts[2] = vertices[index2];
565 GrDrawTarget::Edge tri_edges[6];
566 int t = 0;
567 const GrDrawTarget::Edge& edge0 = edges[index0 * 2];
568 const GrDrawTarget::Edge& edge1 = edges[index0 * 2 + 1];
569 const GrDrawTarget::Edge& edge2 = edges[index1 * 2];
570 const GrDrawTarget::Edge& edge3 = edges[index1 * 2 + 1];
571 const GrDrawTarget::Edge& edge4 = edges[index2 * 2];
572 const GrDrawTarget::Edge& edge5 = edges[index2 * 2 + 1];
573 if (validEdge(edge0) && validEdge(edge1)) {
574 tri_edges[t++] = edge0;
575 tri_edges[t++] = edge1;
576 }
577 if (validEdge(edge2) && validEdge(edge3)) {
578 tri_edges[t++] = edge2;
579 tri_edges[t++] = edge3;
580 }
581 if (validEdge(edge4) && validEdge(edge5)) {
582 tri_edges[t++] = edge4;
583 tri_edges[t++] = edge5;
584 }
bsalomon@google.comee435122011-07-01 14:57:55 +0000585 fTarget->setEdgeAAData(&tri_edges[0], t);
586 fTarget->setVertexSourceToArray(layout, &tri_verts[0], 3);
587 fTarget->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000588 }
bsalomon@google.comee435122011-07-01 14:57:55 +0000589 fTarget->setEdgeAAData(NULL, 0);
590 fTarget->disableState(GrDrawTarget::kEdgeAAConcave_StateBit);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000591 return;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000592 }
593
bsalomon@google.comee435122011-07-01 14:57:55 +0000594 GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fFill));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000595 ptess.addVertices(base, subpathVertCount, subpathCnt);
596 const GrPointArray& vertices = ptess.vertices();
597 const GrIndexArray& indices = ptess.indices();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000598 if (indices.count() > 0) {
bsalomon@google.comee435122011-07-01 14:57:55 +0000599 fTarget->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
600 fTarget->setIndexSourceToArray(indices.begin(), indices.count());
601 fTarget->drawIndexed(kTriangles_PrimitiveType,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000602 0,
603 0,
604 vertices.count(),
605 indices.count());
606 }
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000607}
608
bsalomon@google.comee435122011-07-01 14:57:55 +0000609bool GrTesselatedPathRenderer::canDrawPath(const SkPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000610 GrPathFill fill) const {
611 return kHairLine_PathFill != fill;
612}
613
bsalomon@google.comee435122011-07-01 14:57:55 +0000614void GrTesselatedPathRenderer::drawPathToStencil() {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000615 GrAlwaysAssert(!"multipass stencil should not be needed");
616}
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000617
618bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000619 const SkPath& path,
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000620 GrPathFill fill) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000621 return true;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000622}