blob: c1d5ac0efa7ecc1c5e4039af2920ef0faedbb60f [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
19#include "GrMemory.h"
20#include "GrPathUtils.h"
reed@google.com07f3ee12011-05-16 17:21:57 +000021#include "GrPoint.h"
22#include "GrTDArray.h"
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000023
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000024#include <limits.h>
senorblanco@chromium.org1fa803d2011-05-25 14:46:17 +000025#include <sk_glu.h>
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000026
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000027typedef GrTDArray<GrDrawTarget::Edge> GrEdgeArray;
28typedef GrTDArray<GrPoint> GrPointArray;
29typedef GrTDArray<uint16_t> GrIndexArray;
30typedef void (*TESSCB)();
31
32// limit the allowable vertex range to approximately half of the representable
33// IEEE exponent in order to avoid overflow when doing multiplies between
34// vertex components,
35const float kMaxVertexValue = 1e18;
36
37static inline GrDrawTarget::Edge computeEdge(const GrPoint& p,
38 const GrPoint& q,
39 float sign) {
40 GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
41 float scale = sign / tangent.length();
42 float cross2 = p.fX * q.fY - q.fX * p.fY;
43 return GrDrawTarget::Edge(tangent.fX * scale,
44 tangent.fY * scale,
45 cross2 * scale);
46}
47
48static inline GrPoint sanitizePoint(const GrPoint& pt) {
49 GrPoint r;
50 r.fX = SkScalarPin(pt.fX, -kMaxVertexValue, kMaxVertexValue);
51 r.fY = SkScalarPin(pt.fY, -kMaxVertexValue, kMaxVertexValue);
52 return r;
53}
54
55class GrTess {
56public:
57 GrTess(int count, unsigned winding_rule) {
58 fTess = Sk_gluNewTess();
59 Sk_gluTessProperty(fTess, GLU_TESS_WINDING_RULE, winding_rule);
60 Sk_gluTessNormal(fTess, 0.0f, 0.0f, 1.0f);
61 Sk_gluTessCallback(fTess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginCB);
62 Sk_gluTessCallback(fTess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexCB);
63 Sk_gluTessCallback(fTess, GLU_TESS_END_DATA, (TESSCB) &endCB);
64 Sk_gluTessCallback(fTess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagCB);
65 Sk_gluTessCallback(fTess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineCB);
66 fInVertices = new double[count * 3];
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000067 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000068 ~GrTess() {
69 Sk_gluDeleteTess(fTess);
70 delete[] fInVertices;
71 }
72 void addVertex(const GrPoint& pt, int index) {
73 if (index > USHRT_MAX) return;
74 double* inVertex = &fInVertices[index * 3];
75 inVertex[0] = pt.fX;
76 inVertex[1] = pt.fY;
77 inVertex[2] = 0.0;
78 *fVertices.append() = pt;
79 Sk_gluTessVertex(fTess, inVertex, reinterpret_cast<void*>(index));
80 }
81 void addVertices(const GrPoint* points, const uint16_t* contours, int numContours) {
82 Sk_gluTessBeginPolygon(fTess, this);
83 size_t i = 0;
84 for (int j = 0; j < numContours; ++j) {
85 Sk_gluTessBeginContour(fTess);
86 size_t end = i + contours[j];
87 for (; i < end; ++i) {
88 addVertex(points[i], i);
89 }
90 Sk_gluTessEndContour(fTess);
91 }
92 Sk_gluTessEndPolygon(fTess);
93 }
94 GLUtesselator* tess() { return fTess; }
95 const GrPointArray& vertices() const { return fVertices; }
96protected:
97 virtual void begin(GLenum type) = 0;
98 virtual void vertex(int index) = 0;
99 virtual void edgeFlag(bool flag) = 0;
100 virtual void end() = 0;
101 virtual int combine(GLdouble coords[3], int vertexIndices[4],
102 GLfloat weight[4]) = 0;
103 static void beginCB(GLenum type, void* data) {
104 static_cast<GrTess*>(data)->begin(type);
105 }
106 static void vertexCB(void* vertexData, void* data) {
107 static_cast<GrTess*>(data)->vertex(reinterpret_cast<long>(vertexData));
108 }
109 static void edgeFlagCB(GLboolean flag, void* data) {
110 static_cast<GrTess*>(data)->edgeFlag(flag != 0);
111 }
112 static void endCB(void* data) {
113 static_cast<GrTess*>(data)->end();
114 }
115 static void combineCB(GLdouble coords[3], void* vertexData[4],
116 GLfloat weight[4], void **outData, void* data) {
117 int vertexIndex[4];
118 vertexIndex[0] = reinterpret_cast<long>(vertexData[0]);
119 vertexIndex[1] = reinterpret_cast<long>(vertexData[1]);
120 vertexIndex[2] = reinterpret_cast<long>(vertexData[2]);
121 vertexIndex[3] = reinterpret_cast<long>(vertexData[3]);
122 GrTess* tess = static_cast<GrTess*>(data);
123 int outIndex = tess->combine(coords, vertexIndex, weight);
124 *reinterpret_cast<long*>(outData) = outIndex;
125 }
126protected:
127 GLUtesselator* fTess;
128 GrPointArray fVertices;
129 double* fInVertices;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000130};
131
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000132class GrPolygonTess : public GrTess {
133public:
134 GrPolygonTess(int count, unsigned winding_rule)
135 : GrTess(count, winding_rule) {
136 }
137 ~GrPolygonTess() {
138 }
139 const GrIndexArray& indices() const { return fIndices; }
140protected:
141 virtual void begin(GLenum type) {
142 GR_DEBUGASSERT(type == GL_TRIANGLES);
143 }
144 virtual void vertex(int index) {
145 *fIndices.append() = index;
146 }
147 virtual void edgeFlag(bool flag) {}
148 virtual void end() {}
149 virtual int combine(GLdouble coords[3], int vertexIndices[4],
150 GLfloat weight[4]) {
151 int index = fVertices.count();
152 GrPoint p = GrPoint::Make(static_cast<float>(coords[0]),
153 static_cast<float>(coords[1]));
154 *fVertices.append() = p;
155 return index;
156 }
157protected:
158 GrIndexArray fIndices;
159};
160
161class GrEdgePolygonTess : public GrPolygonTess {
162public:
163 GrEdgePolygonTess(int count, unsigned winding_rule, const SkMatrix& matrix)
164 : GrPolygonTess(count, winding_rule),
165 fMatrix(matrix),
166 fEdgeFlag(false),
167 fEdgeVertex(-1),
168 fTriStartVertex(-1),
169 fEdges(NULL) {
170 }
171 ~GrEdgePolygonTess() {
172 delete[] fEdges;
173 }
174 const GrDrawTarget::Edge* edges() const { return fEdges; }
175private:
176 void addEdge(int index0, int index1) {
177 GrPoint p = fVertices[index0];
178 GrPoint q = fVertices[index1];
179 fMatrix.mapPoints(&p, 1);
180 fMatrix.mapPoints(&q, 1);
181 p = sanitizePoint(p);
182 q = sanitizePoint(q);
183 if (p == q) return;
184 GrDrawTarget::Edge edge = computeEdge(p, q, 1.0f);
185 fEdges[index0 * 2 + 1] = edge;
186 fEdges[index1 * 2] = edge;
187 }
188 virtual void begin(GLenum type) {
189 GR_DEBUGASSERT(type == GL_TRIANGLES);
190 int count = fVertices.count() * 2;
191 fEdges = new GrDrawTarget::Edge[count];
192 memset(fEdges, 0, count * sizeof(GrDrawTarget::Edge));
193 }
194 virtual void edgeFlag(bool flag) {
195 fEdgeFlag = flag;
196 }
197 virtual void vertex(int index) {
198 bool triStart = fIndices.count() % 3 == 0;
199 GrPolygonTess::vertex(index);
200 if (fEdgeVertex != -1) {
201 if (triStart) {
202 addEdge(fEdgeVertex, fTriStartVertex);
203 } else {
204 addEdge(fEdgeVertex, index);
205 }
206 }
207 if (triStart) {
208 fTriStartVertex = index;
209 }
210 if (fEdgeFlag) {
211 fEdgeVertex = index;
212 } else {
213 fEdgeVertex = -1;
214 }
215 }
216 virtual void end() {
217 if (fEdgeVertex != -1) {
218 addEdge(fEdgeVertex, fTriStartVertex);
219 }
220 }
221 GrMatrix fMatrix;
222 bool fEdgeFlag;
223 int fEdgeVertex, fTriStartVertex;
224 GrDrawTarget::Edge* fEdges;
225};
226
227class GrBoundaryTess : public GrTess {
228public:
229 GrBoundaryTess(int count, unsigned winding_rule)
230 : GrTess(count, winding_rule),
231 fContourStart(0) {
232 Sk_gluTessProperty(fTess, GLU_TESS_BOUNDARY_ONLY, 1);
233 }
234 ~GrBoundaryTess() {
235 }
236 GrPointArray& contourPoints() { return fContourPoints; }
237 const GrIndexArray& contours() const { return fContours; }
238private:
239 virtual void begin(GLenum type) {
240 fContourStart = fContourPoints.count();
241 }
242 virtual void vertex(int index) {
243 *fContourPoints.append() = fVertices.at(index);
244 }
245 virtual void edgeFlag(bool flag) {}
246 virtual void end() {
247 *fContours.append() = fContourPoints.count() - fContourStart;
248 }
249 virtual int combine(GLdouble coords[3], int vertexIndices[4],
250 GLfloat weight[4]) {
251 int index = fVertices.count();
252 *fVertices.append() = GrPoint::Make(static_cast<float>(coords[0]),
253 static_cast<float>(coords[1]));
254 return index;
255 }
256 GrPointArray fContourPoints;
257 GrIndexArray fContours;
258 size_t fContourStart;
259};
260
261static bool nearlyEqual(float a, float b) {
262 return fabsf(a - b) < 0.0001f;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000263}
264
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000265static bool nearlyEqual(const GrPoint& a, const GrPoint& b) {
266 return nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000267}
268
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000269static bool parallel(const GrDrawTarget::Edge& a, const GrDrawTarget::Edge& b) {
270 return (nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY)) ||
271 (nearlyEqual(a.fX, -b.fX) && nearlyEqual(a.fY, -b.fY));
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000272}
273
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000274static unsigned fill_type_to_glu_winding_rule(GrPathFill fill) {
275 switch (fill) {
276 case kWinding_PathFill:
277 return GLU_TESS_WINDING_NONZERO;
278 case kEvenOdd_PathFill:
279 return GLU_TESS_WINDING_ODD;
280 case kInverseWinding_PathFill:
281 return GLU_TESS_WINDING_POSITIVE;
282 case kInverseEvenOdd_PathFill:
283 return GLU_TESS_WINDING_ODD;
284 case kHairLine_PathFill:
285 return GLU_TESS_WINDING_NONZERO; // FIXME: handle this
286 default:
287 GrAssert(!"Unknown path fill!");
288 return 0;
289 }
290}
291
292GrTesselatedPathRenderer::GrTesselatedPathRenderer() {
293}
294
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000295static bool isCCW(const GrPoint* pts, int count) {
296 GrVec v1, v2;
297 do {
298 v1 = pts[1] - pts[0];
299 v2 = pts[2] - pts[1];
300 pts++;
301 count--;
302 } while (nearlyEqual(v1, v2) && count > 3);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000303 return v1.cross(v2) < 0;
304}
305
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000306static bool validEdge(const GrDrawTarget::Edge& edge) {
307 return !(edge.fX == 0.0f && edge.fY == 0.0f && edge.fZ == 0.0f);
308}
309
310static size_t computeEdgesAndIntersect(const GrMatrix& matrix,
311 const GrMatrix& inverse,
312 GrPoint* vertices,
313 size_t numVertices,
314 GrEdgeArray* edges,
315 float sign) {
316 if (numVertices < 3) {
317 return 0;
318 }
senorblanco@chromium.orgff174b32011-05-16 16:59:57 +0000319 matrix.mapPoints(vertices, numVertices);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000320 if (sign == 0.0f) {
321 sign = isCCW(vertices, numVertices) ? -1.0f : 1.0f;
322 }
323 GrPoint p = sanitizePoint(vertices[numVertices - 1]);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000324 for (size_t i = 0; i < numVertices; ++i) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000325 GrPoint q = sanitizePoint(vertices[i]);
326 if (p == q) {
327 continue;
328 }
329 GrDrawTarget::Edge edge = computeEdge(p, q, sign);
330 edge.fZ += 0.5f; // Offset by half a pixel along the tangent.
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000331 *edges->append() = edge;
332 p = q;
333 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000334 int count = edges->count();
335 if (count == 0) {
336 return 0;
337 }
338 GrDrawTarget::Edge prev_edge = edges->at(0);
339 for (int i = 0; i < count; ++i) {
340 GrDrawTarget::Edge edge = edges->at(i < count - 1 ? i + 1 : 0);
341 if (parallel(edge, prev_edge)) {
342 // 3 points are collinear; offset by half the tangent instead
343 vertices[i].fX -= edge.fX * 0.5f;
344 vertices[i].fY -= edge.fY * 0.5f;
345 } else {
346 vertices[i] = prev_edge.intersect(edge);
347 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000348 inverse.mapPoints(&vertices[i], 1);
349 prev_edge = edge;
350 }
351 return edges->count();
352}
353
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000354void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
355 GrDrawTarget::StageBitfield stages,
reed@google.com07f3ee12011-05-16 17:21:57 +0000356 const GrPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000357 GrPathFill fill,
358 const GrPoint* translate) {
359 GrDrawTarget::AutoStateRestore asr(target);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000360 // face culling doesn't make sense here
361 GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
362
363 GrMatrix viewM = target->getViewMatrix();
364 // In order to tesselate the path we get a bound on how much the matrix can
365 // stretch when mapping to screen coordinates.
366 GrScalar stretch = viewM.getMaxStretch();
367 bool useStretch = stretch > 0;
tomhudson@google.comd22b6e42011-06-24 15:53:40 +0000368 GrScalar tol = fCurveTolerance;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000369
370 if (!useStretch) {
371 // TODO: deal with perspective in some better way.
372 tol /= 10;
373 } else {
bungeman@google.com8c5753e2011-05-20 19:11:50 +0000374 tol = GrScalarDiv(tol, stretch);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000375 }
376 GrScalar tolSqd = GrMul(tol, tol);
377
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000378 int subpathCnt;
379 int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
380
381 GrVertexLayout layout = 0;
382 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
383 if ((1 << s) & stages) {
384 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
385 }
386 }
387
388 bool inverted = IsFillInverted(fill);
389 if (inverted) {
390 maxPts += 4;
391 subpathCnt++;
392 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000393 if (maxPts > USHRT_MAX) {
394 return;
395 }
396 GrAutoSTMalloc<8, GrPoint> baseMem(maxPts);
397 GrPoint* base = (GrPoint*) baseMem;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000398 GrPoint* vert = base;
399 GrPoint* subpathBase = base;
400
401 GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
402
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000403 GrPoint pts[4];
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000404 SkPath::Iter iter(path, false);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000405
406 bool first = true;
407 int subpath = 0;
408
409 for (;;) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000410 switch (iter.next(pts)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000411 case kMove_PathCmd:
412 if (!first) {
413 subpathVertCount[subpath] = vert-subpathBase;
414 subpathBase = vert;
415 ++subpath;
416 }
417 *vert = pts[0];
418 vert++;
419 break;
420 case kLine_PathCmd:
421 *vert = pts[1];
422 vert++;
423 break;
424 case kQuadratic_PathCmd: {
425 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
426 tolSqd, &vert,
427 GrPathUtils::quadraticPointCount(pts, tol));
428 break;
429 }
430 case kCubic_PathCmd: {
431 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
432 tolSqd, &vert,
433 GrPathUtils::cubicPointCount(pts, tol));
434 break;
435 }
436 case kClose_PathCmd:
437 break;
438 case kEnd_PathCmd:
439 subpathVertCount[subpath] = vert-subpathBase;
440 ++subpath; // this could be only in debug
441 goto FINISHED;
442 }
443 first = false;
444 }
445FINISHED:
446 if (translate) {
447 for (int i = 0; i < vert - base; i++) {
448 base[i].offset(translate->fX, translate->fY);
449 }
450 }
451
452 if (inverted) {
453 GrRect bounds;
454 GrAssert(NULL != target->getRenderTarget());
455 bounds.setLTRB(0, 0,
456 GrIntToScalar(target->getRenderTarget()->width()),
457 GrIntToScalar(target->getRenderTarget()->height()));
458 GrMatrix vmi;
459 if (target->getViewInverse(&vmi)) {
460 vmi.mapRect(&bounds);
461 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000462 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
463 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
464 *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
465 *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000466 subpathVertCount[subpath++] = 4;
467 }
468
469 GrAssert(subpath == subpathCnt);
470 GrAssert((vert - base) <= maxPts);
471
472 size_t count = vert - base;
473
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000474 if (count < 3) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000475 return;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000476 }
477
reed@google.com07f3ee12011-05-16 17:21:57 +0000478 if (subpathCnt == 1 && !inverted && path.isConvex()) {
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000479 if (target->isAntialiasState()) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000480 GrEdgeArray edges;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000481 GrMatrix inverse, matrix = target->getViewMatrix();
482 target->getViewInverse(&inverse);
483
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000484 count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f);
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000485 size_t maxEdges = target->getMaxEdges();
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000486 if (count == 0) {
487 return;
488 }
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000489 if (count <= maxEdges) {
490 // All edges fit; upload all edges and draw all verts as a fan
491 target->setVertexSourceToArray(layout, base, count);
492 target->setEdgeAAData(&edges[0], count);
493 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
494 } else {
495 // Upload "maxEdges" edges and verts at a time, and draw as
496 // separate fans
497 for (size_t i = 0; i < count - 2; i += maxEdges - 2) {
498 edges[i] = edges[0];
499 base[i] = base[0];
500 int size = GR_CT_MIN(count - i, maxEdges);
501 target->setVertexSourceToArray(layout, &base[i], size);
502 target->setEdgeAAData(&edges[i], size);
503 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
504 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000505 }
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000506 target->setEdgeAAData(NULL, 0);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000507 } else {
508 target->setVertexSourceToArray(layout, base, count);
509 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
510 }
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000511 return;
512 }
513
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000514 if (target->isAntialiasState()) {
515 // Run the tesselator once to get the boundaries.
516 GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fill));
517 btess.addVertices(base, subpathVertCount, subpathCnt);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000518
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000519 GrMatrix inverse, matrix = target->getViewMatrix();
520 if (!target->getViewInverse(&inverse)) {
521 return;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000522 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000523
524 if (btess.vertices().count() > USHRT_MAX) {
525 return;
526 }
527
528 // Inflate the boundary, and run the tesselator again to generate
529 // interior polys.
530 const GrPointArray& contourPoints = btess.contourPoints();
531 const GrIndexArray& contours = btess.contours();
532 GrEdgePolygonTess ptess(contourPoints.count(), GLU_TESS_WINDING_NONZERO, matrix);
533
534 size_t i = 0;
535 Sk_gluTessBeginPolygon(ptess.tess(), &ptess);
536 for (int contour = 0; contour < contours.count(); ++contour) {
537 int count = contours[contour];
538 GrEdgeArray edges;
539 int newCount = computeEdgesAndIntersect(matrix, inverse, &btess.contourPoints()[i], count, &edges, 1.0f);
540 Sk_gluTessBeginContour(ptess.tess());
541 for (int j = 0; j < newCount; j++) {
542 ptess.addVertex(contourPoints[i + j], ptess.vertices().count());
543 }
544 i += count;
545 Sk_gluTessEndContour(ptess.tess());
546 }
547
548 Sk_gluTessEndPolygon(ptess.tess());
549
550 if (ptess.vertices().count() > USHRT_MAX) {
551 return;
552 }
553
554 // Draw the resulting polys and upload their edge data.
555 target->enableState(GrDrawTarget::kEdgeAAConcave_StateBit);
556 const GrPointArray& vertices = ptess.vertices();
557 const GrIndexArray& indices = ptess.indices();
558 const GrDrawTarget::Edge* edges = ptess.edges();
559 GR_DEBUGASSERT(indices.count() % 3 == 0);
560 for (int i = 0; i < indices.count(); i += 3) {
561 GrPoint tri_verts[3];
562 int index0 = indices[i];
563 int index1 = indices[i + 1];
564 int index2 = indices[i + 2];
565 tri_verts[0] = vertices[index0];
566 tri_verts[1] = vertices[index1];
567 tri_verts[2] = vertices[index2];
568 GrDrawTarget::Edge tri_edges[6];
569 int t = 0;
570 const GrDrawTarget::Edge& edge0 = edges[index0 * 2];
571 const GrDrawTarget::Edge& edge1 = edges[index0 * 2 + 1];
572 const GrDrawTarget::Edge& edge2 = edges[index1 * 2];
573 const GrDrawTarget::Edge& edge3 = edges[index1 * 2 + 1];
574 const GrDrawTarget::Edge& edge4 = edges[index2 * 2];
575 const GrDrawTarget::Edge& edge5 = edges[index2 * 2 + 1];
576 if (validEdge(edge0) && validEdge(edge1)) {
577 tri_edges[t++] = edge0;
578 tri_edges[t++] = edge1;
579 }
580 if (validEdge(edge2) && validEdge(edge3)) {
581 tri_edges[t++] = edge2;
582 tri_edges[t++] = edge3;
583 }
584 if (validEdge(edge4) && validEdge(edge5)) {
585 tri_edges[t++] = edge4;
586 tri_edges[t++] = edge5;
587 }
588 target->setEdgeAAData(&tri_edges[0], t);
589 target->setVertexSourceToArray(layout, &tri_verts[0], 3);
590 target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
591 }
592 target->setEdgeAAData(NULL, 0);
593 target->disableState(GrDrawTarget::kEdgeAAConcave_StateBit);
594 return;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000595 }
596
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000597 GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fill));
598 ptess.addVertices(base, subpathVertCount, subpathCnt);
599 const GrPointArray& vertices = ptess.vertices();
600 const GrIndexArray& indices = ptess.indices();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000601 if (indices.count() > 0) {
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000602 target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
603 target->setIndexSourceToArray(indices.begin(), indices.count());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000604 target->drawIndexed(kTriangles_PrimitiveType,
605 0,
606 0,
607 vertices.count(),
608 indices.count());
609 }
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000610}
611
612bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000613 const SkPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000614 GrPathFill fill) const {
615 return kHairLine_PathFill != fill;
616}
617
618void GrTesselatedPathRenderer::drawPathToStencil(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000619 const SkPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000620 GrPathFill fill,
621 const GrPoint* translate) {
622 GrAlwaysAssert(!"multipass stencil should not be needed");
623}
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000624
625bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000626 const SkPath& path,
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000627 GrPathFill fill) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000628 return true;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000629}