blob: f6fcdef5875f8eb8b621c8bd4b87895c2fc8cd11 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +00007 */
8
epoger@google.comec3ed6a2011-07-28 14:26:00 +00009
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000010#include "GrTesselatedPathRenderer.h"
11
tomhudson@google.com93813632011-10-27 20:21:16 +000012#include "GrDrawState.h"
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000013#include "GrPathUtils.h"
reed@google.com07f3ee12011-05-16 17:21:57 +000014#include "GrPoint.h"
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +000015#include "GrRenderTarget.h"
reed@google.com07f3ee12011-05-16 17:21:57 +000016#include "GrTDArray.h"
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000017
bsalomon@google.com3582bf92011-06-30 21:32:31 +000018#include "SkTemplates.h"
19
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000020#include <limits.h>
senorblanco@chromium.org1fa803d2011-05-25 14:46:17 +000021#include <sk_glu.h>
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000022
tomhudson@google.com93813632011-10-27 20:21:16 +000023typedef GrTDArray<GrDrawState::Edge> GrEdgeArray;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000024typedef GrTDArray<GrPoint> GrPointArray;
25typedef GrTDArray<uint16_t> GrIndexArray;
26typedef void (*TESSCB)();
27
28// limit the allowable vertex range to approximately half of the representable
29// IEEE exponent in order to avoid overflow when doing multiplies between
30// vertex components,
bsalomon@google.comee435122011-07-01 14:57:55 +000031const float kMaxVertexValue = 1e18f;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000032
tomhudson@google.com93813632011-10-27 20:21:16 +000033static inline GrDrawState::Edge computeEdge(const GrPoint& p,
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000034 const GrPoint& q,
35 float sign) {
36 GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
37 float scale = sign / tangent.length();
38 float cross2 = p.fX * q.fY - q.fX * p.fY;
tomhudson@google.com93813632011-10-27 20:21:16 +000039 return GrDrawState::Edge(tangent.fX * scale,
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000040 tangent.fY * scale,
41 cross2 * scale);
42}
43
44static inline GrPoint sanitizePoint(const GrPoint& pt) {
45 GrPoint r;
46 r.fX = SkScalarPin(pt.fX, -kMaxVertexValue, kMaxVertexValue);
47 r.fY = SkScalarPin(pt.fY, -kMaxVertexValue, kMaxVertexValue);
48 return r;
49}
50
51class GrTess {
52public:
53 GrTess(int count, unsigned winding_rule) {
54 fTess = Sk_gluNewTess();
55 Sk_gluTessProperty(fTess, GLU_TESS_WINDING_RULE, winding_rule);
56 Sk_gluTessNormal(fTess, 0.0f, 0.0f, 1.0f);
57 Sk_gluTessCallback(fTess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginCB);
58 Sk_gluTessCallback(fTess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexCB);
59 Sk_gluTessCallback(fTess, GLU_TESS_END_DATA, (TESSCB) &endCB);
60 Sk_gluTessCallback(fTess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagCB);
61 Sk_gluTessCallback(fTess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineCB);
62 fInVertices = new double[count * 3];
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000063 }
djsollen@google.com60abb072012-02-15 18:49:15 +000064 virtual ~GrTess() {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000065 Sk_gluDeleteTess(fTess);
66 delete[] fInVertices;
67 }
68 void addVertex(const GrPoint& pt, int index) {
69 if (index > USHRT_MAX) return;
70 double* inVertex = &fInVertices[index * 3];
71 inVertex[0] = pt.fX;
72 inVertex[1] = pt.fY;
73 inVertex[2] = 0.0;
74 *fVertices.append() = pt;
75 Sk_gluTessVertex(fTess, inVertex, reinterpret_cast<void*>(index));
76 }
77 void addVertices(const GrPoint* points, const uint16_t* contours, int numContours) {
78 Sk_gluTessBeginPolygon(fTess, this);
79 size_t i = 0;
80 for (int j = 0; j < numContours; ++j) {
81 Sk_gluTessBeginContour(fTess);
82 size_t end = i + contours[j];
83 for (; i < end; ++i) {
84 addVertex(points[i], i);
85 }
86 Sk_gluTessEndContour(fTess);
87 }
88 Sk_gluTessEndPolygon(fTess);
89 }
90 GLUtesselator* tess() { return fTess; }
91 const GrPointArray& vertices() const { return fVertices; }
92protected:
93 virtual void begin(GLenum type) = 0;
94 virtual void vertex(int index) = 0;
95 virtual void edgeFlag(bool flag) = 0;
96 virtual void end() = 0;
97 virtual int combine(GLdouble coords[3], int vertexIndices[4],
98 GLfloat weight[4]) = 0;
99 static void beginCB(GLenum type, void* data) {
100 static_cast<GrTess*>(data)->begin(type);
101 }
102 static void vertexCB(void* vertexData, void* data) {
103 static_cast<GrTess*>(data)->vertex(reinterpret_cast<long>(vertexData));
104 }
105 static void edgeFlagCB(GLboolean flag, void* data) {
106 static_cast<GrTess*>(data)->edgeFlag(flag != 0);
107 }
108 static void endCB(void* data) {
109 static_cast<GrTess*>(data)->end();
110 }
111 static void combineCB(GLdouble coords[3], void* vertexData[4],
112 GLfloat weight[4], void **outData, void* data) {
113 int vertexIndex[4];
114 vertexIndex[0] = reinterpret_cast<long>(vertexData[0]);
115 vertexIndex[1] = reinterpret_cast<long>(vertexData[1]);
116 vertexIndex[2] = reinterpret_cast<long>(vertexData[2]);
117 vertexIndex[3] = reinterpret_cast<long>(vertexData[3]);
118 GrTess* tess = static_cast<GrTess*>(data);
119 int outIndex = tess->combine(coords, vertexIndex, weight);
120 *reinterpret_cast<long*>(outData) = outIndex;
121 }
122protected:
123 GLUtesselator* fTess;
124 GrPointArray fVertices;
125 double* fInVertices;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000126};
127
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000128class GrPolygonTess : public GrTess {
129public:
130 GrPolygonTess(int count, unsigned winding_rule)
131 : GrTess(count, winding_rule) {
132 }
133 ~GrPolygonTess() {
134 }
135 const GrIndexArray& indices() const { return fIndices; }
136protected:
137 virtual void begin(GLenum type) {
138 GR_DEBUGASSERT(type == GL_TRIANGLES);
139 }
140 virtual void vertex(int index) {
141 *fIndices.append() = index;
142 }
143 virtual void edgeFlag(bool flag) {}
144 virtual void end() {}
145 virtual int combine(GLdouble coords[3], int vertexIndices[4],
146 GLfloat weight[4]) {
147 int index = fVertices.count();
148 GrPoint p = GrPoint::Make(static_cast<float>(coords[0]),
149 static_cast<float>(coords[1]));
150 *fVertices.append() = p;
151 return index;
152 }
153protected:
154 GrIndexArray fIndices;
155};
156
157class GrEdgePolygonTess : public GrPolygonTess {
158public:
159 GrEdgePolygonTess(int count, unsigned winding_rule, const SkMatrix& matrix)
160 : GrPolygonTess(count, winding_rule),
161 fMatrix(matrix),
162 fEdgeFlag(false),
163 fEdgeVertex(-1),
164 fTriStartVertex(-1),
165 fEdges(NULL) {
166 }
167 ~GrEdgePolygonTess() {
168 delete[] fEdges;
169 }
tomhudson@google.com93813632011-10-27 20:21:16 +0000170 const GrDrawState::Edge* edges() const { return fEdges; }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000171private:
172 void addEdge(int index0, int index1) {
173 GrPoint p = fVertices[index0];
174 GrPoint q = fVertices[index1];
175 fMatrix.mapPoints(&p, 1);
176 fMatrix.mapPoints(&q, 1);
177 p = sanitizePoint(p);
178 q = sanitizePoint(q);
179 if (p == q) return;
tomhudson@google.com93813632011-10-27 20:21:16 +0000180 GrDrawState::Edge edge = computeEdge(p, q, 1.0f);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000181 fEdges[index0 * 2 + 1] = edge;
182 fEdges[index1 * 2] = edge;
183 }
184 virtual void begin(GLenum type) {
185 GR_DEBUGASSERT(type == GL_TRIANGLES);
186 int count = fVertices.count() * 2;
tomhudson@google.com93813632011-10-27 20:21:16 +0000187 fEdges = new GrDrawState::Edge[count];
188 memset(fEdges, 0, count * sizeof(GrDrawState::Edge));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000189 }
190 virtual void edgeFlag(bool flag) {
191 fEdgeFlag = flag;
192 }
193 virtual void vertex(int index) {
194 bool triStart = fIndices.count() % 3 == 0;
195 GrPolygonTess::vertex(index);
196 if (fEdgeVertex != -1) {
197 if (triStart) {
198 addEdge(fEdgeVertex, fTriStartVertex);
199 } else {
200 addEdge(fEdgeVertex, index);
201 }
202 }
203 if (triStart) {
204 fTriStartVertex = index;
205 }
206 if (fEdgeFlag) {
207 fEdgeVertex = index;
208 } else {
209 fEdgeVertex = -1;
210 }
211 }
212 virtual void end() {
213 if (fEdgeVertex != -1) {
214 addEdge(fEdgeVertex, fTriStartVertex);
215 }
216 }
217 GrMatrix fMatrix;
218 bool fEdgeFlag;
219 int fEdgeVertex, fTriStartVertex;
tomhudson@google.com93813632011-10-27 20:21:16 +0000220 GrDrawState::Edge* fEdges;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000221};
222
223class GrBoundaryTess : public GrTess {
224public:
225 GrBoundaryTess(int count, unsigned winding_rule)
226 : GrTess(count, winding_rule),
227 fContourStart(0) {
228 Sk_gluTessProperty(fTess, GLU_TESS_BOUNDARY_ONLY, 1);
229 }
230 ~GrBoundaryTess() {
231 }
232 GrPointArray& contourPoints() { return fContourPoints; }
233 const GrIndexArray& contours() const { return fContours; }
234private:
235 virtual void begin(GLenum type) {
236 fContourStart = fContourPoints.count();
237 }
238 virtual void vertex(int index) {
239 *fContourPoints.append() = fVertices.at(index);
240 }
241 virtual void edgeFlag(bool flag) {}
242 virtual void end() {
243 *fContours.append() = fContourPoints.count() - fContourStart;
244 }
245 virtual int combine(GLdouble coords[3], int vertexIndices[4],
246 GLfloat weight[4]) {
247 int index = fVertices.count();
248 *fVertices.append() = GrPoint::Make(static_cast<float>(coords[0]),
249 static_cast<float>(coords[1]));
250 return index;
251 }
252 GrPointArray fContourPoints;
253 GrIndexArray fContours;
254 size_t fContourStart;
255};
256
257static bool nearlyEqual(float a, float b) {
258 return fabsf(a - b) < 0.0001f;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000259}
260
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000261static bool nearlyEqual(const GrPoint& a, const GrPoint& b) {
262 return nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000263}
264
tomhudson@google.com93813632011-10-27 20:21:16 +0000265static bool parallel(const GrDrawState::Edge& a, const GrDrawState::Edge& b) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000266 return (nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY)) ||
267 (nearlyEqual(a.fX, -b.fX) && nearlyEqual(a.fY, -b.fY));
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000268}
269
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000270static unsigned fill_type_to_glu_winding_rule(GrPathFill fill) {
271 switch (fill) {
272 case kWinding_PathFill:
273 return GLU_TESS_WINDING_NONZERO;
274 case kEvenOdd_PathFill:
275 return GLU_TESS_WINDING_ODD;
276 case kInverseWinding_PathFill:
277 return GLU_TESS_WINDING_POSITIVE;
278 case kInverseEvenOdd_PathFill:
279 return GLU_TESS_WINDING_ODD;
280 case kHairLine_PathFill:
281 return GLU_TESS_WINDING_NONZERO; // FIXME: handle this
282 default:
283 GrAssert(!"Unknown path fill!");
284 return 0;
285 }
286}
287
288GrTesselatedPathRenderer::GrTesselatedPathRenderer() {
289}
290
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000291static bool isCCW(const GrPoint* pts, int count) {
292 GrVec v1, v2;
293 do {
294 v1 = pts[1] - pts[0];
295 v2 = pts[2] - pts[1];
296 pts++;
297 count--;
298 } while (nearlyEqual(v1, v2) && count > 3);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000299 return v1.cross(v2) < 0;
300}
301
tomhudson@google.com93813632011-10-27 20:21:16 +0000302static bool validEdge(const GrDrawState::Edge& edge) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000303 return !(edge.fX == 0.0f && edge.fY == 0.0f && edge.fZ == 0.0f);
304}
305
306static size_t computeEdgesAndIntersect(const GrMatrix& matrix,
307 const GrMatrix& inverse,
308 GrPoint* vertices,
309 size_t numVertices,
310 GrEdgeArray* edges,
311 float sign) {
312 if (numVertices < 3) {
313 return 0;
314 }
senorblanco@chromium.orgff174b32011-05-16 16:59:57 +0000315 matrix.mapPoints(vertices, numVertices);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000316 if (sign == 0.0f) {
317 sign = isCCW(vertices, numVertices) ? -1.0f : 1.0f;
318 }
319 GrPoint p = sanitizePoint(vertices[numVertices - 1]);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000320 for (size_t i = 0; i < numVertices; ++i) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000321 GrPoint q = sanitizePoint(vertices[i]);
322 if (p == q) {
323 continue;
324 }
tomhudson@google.com93813632011-10-27 20:21:16 +0000325 GrDrawState::Edge edge = computeEdge(p, q, sign);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000326 edge.fZ += 0.5f; // Offset by half a pixel along the tangent.
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000327 *edges->append() = edge;
328 p = q;
329 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000330 int count = edges->count();
331 if (count == 0) {
332 return 0;
333 }
tomhudson@google.com93813632011-10-27 20:21:16 +0000334 GrDrawState::Edge prev_edge = edges->at(0);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000335 for (int i = 0; i < count; ++i) {
tomhudson@google.com93813632011-10-27 20:21:16 +0000336 GrDrawState::Edge edge = edges->at(i < count - 1 ? i + 1 : 0);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000337 if (parallel(edge, prev_edge)) {
338 // 3 points are collinear; offset by half the tangent instead
339 vertices[i].fX -= edge.fX * 0.5f;
340 vertices[i].fY -= edge.fY * 0.5f;
341 } else {
342 vertices[i] = prev_edge.intersect(edge);
343 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000344 inverse.mapPoints(&vertices[i], 1);
345 prev_edge = edge;
346 }
347 return edges->count();
348}
349
bsalomon@google.com39ee0ff2011-12-06 15:32:52 +0000350void GrTesselatedPathRenderer::drawPath(GrDrawState::StageMask stageMask) {
bsalomon@google.comee435122011-07-01 14:57:55 +0000351 GrDrawTarget::AutoStateRestore asr(fTarget);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000352 GrDrawState* drawState = fTarget->drawState();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000353 // face culling doesn't make sense here
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000354 GrAssert(GrDrawState::kBoth_DrawFace == drawState->getDrawFace());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000355
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000356 GrMatrix viewM = drawState->getViewMatrix();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000357
bsalomon@google.com181e9bd2011-09-07 18:42:30 +0000358 GrScalar tol = GR_Scalar1;
bsalomon@google.com38396322011-09-09 19:32:04 +0000359 tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, fPath->getBounds());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000360 GrScalar tolSqd = GrMul(tol, tol);
361
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000362 int subpathCnt;
bsalomon@google.comee435122011-07-01 14:57:55 +0000363 int maxPts = GrPathUtils::worstCasePointCount(*fPath, &subpathCnt, tol);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000364
365 GrVertexLayout layout = 0;
tomhudson@google.com93813632011-10-27 20:21:16 +0000366 for (int s = 0; s < GrDrawState::kNumStages; ++s) {
bsalomon@google.com39ee0ff2011-12-06 15:32:52 +0000367 if ((1 << s) & stageMask) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000368 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
369 }
370 }
371
bsalomon@google.comfa6ac932011-10-05 19:57:55 +0000372 bool inverted = GrIsFillInverted(fFill);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000373 if (inverted) {
374 maxPts += 4;
375 subpathCnt++;
376 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000377 if (maxPts > USHRT_MAX) {
378 return;
379 }
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000380 SkAutoSTMalloc<8, GrPoint> baseMem(maxPts);
381 GrPoint* base = baseMem;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000382 GrPoint* vert = base;
383 GrPoint* subpathBase = base;
384
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000385 SkAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000386
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000387 GrPoint pts[4];
bsalomon@google.comee435122011-07-01 14:57:55 +0000388 SkPath::Iter iter(*fPath, false);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000389
390 bool first = true;
391 int subpath = 0;
392
393 for (;;) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000394 switch (iter.next(pts)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000395 case kMove_PathCmd:
396 if (!first) {
397 subpathVertCount[subpath] = vert-subpathBase;
398 subpathBase = vert;
399 ++subpath;
400 }
401 *vert = pts[0];
402 vert++;
403 break;
404 case kLine_PathCmd:
405 *vert = pts[1];
406 vert++;
407 break;
408 case kQuadratic_PathCmd: {
409 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
410 tolSqd, &vert,
411 GrPathUtils::quadraticPointCount(pts, tol));
412 break;
413 }
414 case kCubic_PathCmd: {
415 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
416 tolSqd, &vert,
417 GrPathUtils::cubicPointCount(pts, tol));
418 break;
419 }
420 case kClose_PathCmd:
421 break;
422 case kEnd_PathCmd:
423 subpathVertCount[subpath] = vert-subpathBase;
424 ++subpath; // this could be only in debug
425 goto FINISHED;
426 }
427 first = false;
428 }
429FINISHED:
bsalomon@google.comee435122011-07-01 14:57:55 +0000430 if (0 != fTranslate.fX || 0 != fTranslate.fY) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000431 for (int i = 0; i < vert - base; i++) {
bsalomon@google.comee435122011-07-01 14:57:55 +0000432 base[i].offset(fTranslate.fX, fTranslate.fY);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000433 }
434 }
435
436 if (inverted) {
437 GrRect bounds;
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000438 GrAssert(NULL != drawState->getRenderTarget());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000439 bounds.setLTRB(0, 0,
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000440 GrIntToScalar(drawState->getRenderTarget()->width()),
441 GrIntToScalar(drawState->getRenderTarget()->height()));
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000442 GrMatrix vmi;
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000443 if (drawState->getViewInverse(&vmi)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000444 vmi.mapRect(&bounds);
445 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000446 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
447 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
448 *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
449 *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000450 subpathVertCount[subpath++] = 4;
451 }
452
453 GrAssert(subpath == subpathCnt);
454 GrAssert((vert - base) <= maxPts);
455
456 size_t count = vert - base;
457
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000458 if (count < 3) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000459 return;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000460 }
461
bsalomon@google.comee435122011-07-01 14:57:55 +0000462 if (subpathCnt == 1 && !inverted && fPath->isConvex()) {
bsalomon@google.com289533a2011-10-27 12:34:25 +0000463 if (fAntiAlias) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000464 GrEdgeArray edges;
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000465 GrMatrix inverse, matrix = drawState->getViewMatrix();
466 drawState->getViewInverse(&inverse);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000467
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000468 count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f);
bsalomon@google.comee435122011-07-01 14:57:55 +0000469 size_t maxEdges = fTarget->getMaxEdges();
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000470 if (count == 0) {
471 return;
472 }
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000473 if (count <= maxEdges) {
474 // All edges fit; upload all edges and draw all verts as a fan
bsalomon@google.comee435122011-07-01 14:57:55 +0000475 fTarget->setVertexSourceToArray(layout, base, count);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000476 drawState->setEdgeAAData(&edges[0], count);
bsalomon@google.comee435122011-07-01 14:57:55 +0000477 fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000478 } else {
479 // Upload "maxEdges" edges and verts at a time, and draw as
480 // separate fans
481 for (size_t i = 0; i < count - 2; i += maxEdges - 2) {
482 edges[i] = edges[0];
483 base[i] = base[0];
484 int size = GR_CT_MIN(count - i, maxEdges);
bsalomon@google.comee435122011-07-01 14:57:55 +0000485 fTarget->setVertexSourceToArray(layout, &base[i], size);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000486 drawState->setEdgeAAData(&edges[i], size);
bsalomon@google.comee435122011-07-01 14:57:55 +0000487 fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000488 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000489 }
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000490 drawState->setEdgeAAData(NULL, 0);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000491 } else {
bsalomon@google.comee435122011-07-01 14:57:55 +0000492 fTarget->setVertexSourceToArray(layout, base, count);
493 fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000494 }
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000495 return;
496 }
497
bsalomon@google.com289533a2011-10-27 12:34:25 +0000498 if (fAntiAlias) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000499 // Run the tesselator once to get the boundaries.
bsalomon@google.comee435122011-07-01 14:57:55 +0000500 GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fFill));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000501 btess.addVertices(base, subpathVertCount, subpathCnt);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000502
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000503 GrMatrix inverse, matrix = drawState->getViewMatrix();
504 if (!drawState->getViewInverse(&inverse)) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000505 return;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000506 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000507
508 if (btess.vertices().count() > USHRT_MAX) {
509 return;
510 }
511
512 // Inflate the boundary, and run the tesselator again to generate
513 // interior polys.
514 const GrPointArray& contourPoints = btess.contourPoints();
515 const GrIndexArray& contours = btess.contours();
516 GrEdgePolygonTess ptess(contourPoints.count(), GLU_TESS_WINDING_NONZERO, matrix);
517
518 size_t i = 0;
519 Sk_gluTessBeginPolygon(ptess.tess(), &ptess);
520 for (int contour = 0; contour < contours.count(); ++contour) {
521 int count = contours[contour];
522 GrEdgeArray edges;
523 int newCount = computeEdgesAndIntersect(matrix, inverse, &btess.contourPoints()[i], count, &edges, 1.0f);
524 Sk_gluTessBeginContour(ptess.tess());
525 for (int j = 0; j < newCount; j++) {
526 ptess.addVertex(contourPoints[i + j], ptess.vertices().count());
527 }
528 i += count;
529 Sk_gluTessEndContour(ptess.tess());
530 }
531
532 Sk_gluTessEndPolygon(ptess.tess());
533
534 if (ptess.vertices().count() > USHRT_MAX) {
535 return;
536 }
537
538 // Draw the resulting polys and upload their edge data.
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000539 drawState->enableState(GrDrawState::kEdgeAAConcave_StateBit);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000540 const GrPointArray& vertices = ptess.vertices();
541 const GrIndexArray& indices = ptess.indices();
tomhudson@google.com93813632011-10-27 20:21:16 +0000542 const GrDrawState::Edge* edges = ptess.edges();
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000543 GR_DEBUGASSERT(indices.count() % 3 == 0);
544 for (int i = 0; i < indices.count(); i += 3) {
545 GrPoint tri_verts[3];
546 int index0 = indices[i];
547 int index1 = indices[i + 1];
548 int index2 = indices[i + 2];
549 tri_verts[0] = vertices[index0];
550 tri_verts[1] = vertices[index1];
551 tri_verts[2] = vertices[index2];
tomhudson@google.com93813632011-10-27 20:21:16 +0000552 GrDrawState::Edge tri_edges[6];
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000553 int t = 0;
tomhudson@google.com93813632011-10-27 20:21:16 +0000554 const GrDrawState::Edge& edge0 = edges[index0 * 2];
555 const GrDrawState::Edge& edge1 = edges[index0 * 2 + 1];
556 const GrDrawState::Edge& edge2 = edges[index1 * 2];
557 const GrDrawState::Edge& edge3 = edges[index1 * 2 + 1];
558 const GrDrawState::Edge& edge4 = edges[index2 * 2];
559 const GrDrawState::Edge& edge5 = edges[index2 * 2 + 1];
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000560 if (validEdge(edge0) && validEdge(edge1)) {
561 tri_edges[t++] = edge0;
562 tri_edges[t++] = edge1;
563 }
564 if (validEdge(edge2) && validEdge(edge3)) {
565 tri_edges[t++] = edge2;
566 tri_edges[t++] = edge3;
567 }
568 if (validEdge(edge4) && validEdge(edge5)) {
569 tri_edges[t++] = edge4;
570 tri_edges[t++] = edge5;
571 }
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000572 drawState->setEdgeAAData(&tri_edges[0], t);
bsalomon@google.comee435122011-07-01 14:57:55 +0000573 fTarget->setVertexSourceToArray(layout, &tri_verts[0], 3);
574 fTarget->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000575 }
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000576 drawState->setEdgeAAData(NULL, 0);
577 drawState->disableState(GrDrawState::kEdgeAAConcave_StateBit);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000578 return;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000579 }
580
bsalomon@google.comee435122011-07-01 14:57:55 +0000581 GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fFill));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000582 ptess.addVertices(base, subpathVertCount, subpathCnt);
583 const GrPointArray& vertices = ptess.vertices();
584 const GrIndexArray& indices = ptess.indices();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000585 if (indices.count() > 0) {
bsalomon@google.comee435122011-07-01 14:57:55 +0000586 fTarget->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
587 fTarget->setIndexSourceToArray(indices.begin(), indices.count());
588 fTarget->drawIndexed(kTriangles_PrimitiveType,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000589 0,
590 0,
591 vertices.count(),
592 indices.count());
593 }
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000594}
595
bsalomon@google.com289533a2011-10-27 12:34:25 +0000596bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget::Caps& caps,
bsalomon@google.comaeb21602011-08-30 18:13:44 +0000597 const SkPath& path,
bsalomon@google.com289533a2011-10-27 12:34:25 +0000598 GrPathFill fill,
599 bool antiAlias) const {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000600 return kHairLine_PathFill != fill;
601}
602
bsalomon@google.comee435122011-07-01 14:57:55 +0000603void GrTesselatedPathRenderer::drawPathToStencil() {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000604 GrAlwaysAssert(!"multipass stencil should not be needed");
605}
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000606