blob: 3823bbd1c82fbe20976706047bc26d35cdd5b8e7 [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.comc2099d22012-03-02 21:26:50 +0000350bool GrTesselatedPathRenderer::onDrawPath(const SkPath& path,
351 GrPathFill fill,
352 const GrVec* translate,
353 GrDrawTarget* target,
354 GrDrawState::StageMask stageMask,
355 bool antiAlias) {
356
357 GrDrawTarget::AutoStateRestore asr(target);
358 GrDrawState* drawState = target->drawState();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000359 // face culling doesn't make sense here
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000360 GrAssert(GrDrawState::kBoth_DrawFace == drawState->getDrawFace());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000361
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000362 GrMatrix viewM = drawState->getViewMatrix();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000363
bsalomon@google.com181e9bd2011-09-07 18:42:30 +0000364 GrScalar tol = GR_Scalar1;
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000365 tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, path.getBounds());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000366 GrScalar tolSqd = GrMul(tol, tol);
367
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000368 int subpathCnt;
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000369 int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000370
371 GrVertexLayout layout = 0;
tomhudson@google.com93813632011-10-27 20:21:16 +0000372 for (int s = 0; s < GrDrawState::kNumStages; ++s) {
bsalomon@google.com39ee0ff2011-12-06 15:32:52 +0000373 if ((1 << s) & stageMask) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000374 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
375 }
376 }
377
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000378 bool inverted = GrIsFillInverted(fill);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000379 if (inverted) {
380 maxPts += 4;
381 subpathCnt++;
382 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000383 if (maxPts > USHRT_MAX) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000384 return false;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000385 }
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000386 SkAutoSTMalloc<8, GrPoint> baseMem(maxPts);
387 GrPoint* base = baseMem;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000388 GrPoint* vert = base;
389 GrPoint* subpathBase = base;
390
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000391 SkAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000392
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000393 GrPoint pts[4];
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000394 SkPath::Iter iter(path, false);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000395
396 bool first = true;
397 int subpath = 0;
398
399 for (;;) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000400 switch (iter.next(pts)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000401 case kMove_PathCmd:
402 if (!first) {
403 subpathVertCount[subpath] = vert-subpathBase;
404 subpathBase = vert;
405 ++subpath;
406 }
407 *vert = pts[0];
408 vert++;
409 break;
410 case kLine_PathCmd:
411 *vert = pts[1];
412 vert++;
413 break;
414 case kQuadratic_PathCmd: {
415 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
416 tolSqd, &vert,
417 GrPathUtils::quadraticPointCount(pts, tol));
418 break;
419 }
420 case kCubic_PathCmd: {
421 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
422 tolSqd, &vert,
423 GrPathUtils::cubicPointCount(pts, tol));
424 break;
425 }
426 case kClose_PathCmd:
427 break;
428 case kEnd_PathCmd:
429 subpathVertCount[subpath] = vert-subpathBase;
430 ++subpath; // this could be only in debug
431 goto FINISHED;
432 }
433 first = false;
434 }
435FINISHED:
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000436 if (NULL != translate && 0 != translate->fX && 0 != translate->fY) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000437 for (int i = 0; i < vert - base; i++) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000438 base[i].offset(translate->fX, translate->fY);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000439 }
440 }
441
442 if (inverted) {
443 GrRect bounds;
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000444 GrAssert(NULL != drawState->getRenderTarget());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000445 bounds.setLTRB(0, 0,
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000446 GrIntToScalar(drawState->getRenderTarget()->width()),
447 GrIntToScalar(drawState->getRenderTarget()->height()));
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000448 GrMatrix vmi;
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000449 if (drawState->getViewInverse(&vmi)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000450 vmi.mapRect(&bounds);
451 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000452 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
453 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
454 *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
455 *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000456 subpathVertCount[subpath++] = 4;
457 }
458
459 GrAssert(subpath == subpathCnt);
460 GrAssert((vert - base) <= maxPts);
461
462 size_t count = vert - base;
463
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000464 if (count < 3) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000465 return true;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000466 }
467
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000468 if (subpathCnt == 1 && !inverted && path.isConvex()) {
469 if (antiAlias) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000470 GrEdgeArray edges;
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000471 GrMatrix inverse, matrix = drawState->getViewMatrix();
472 drawState->getViewInverse(&inverse);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000473
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000474 count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000475 size_t maxEdges = target->getMaxEdges();
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000476 if (count == 0) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000477 return true;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000478 }
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000479 if (count <= maxEdges) {
480 // All edges fit; upload all edges and draw all verts as a fan
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000481 target->setVertexSourceToArray(layout, base, count);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000482 drawState->setEdgeAAData(&edges[0], count);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000483 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000484 } else {
485 // Upload "maxEdges" edges and verts at a time, and draw as
486 // separate fans
487 for (size_t i = 0; i < count - 2; i += maxEdges - 2) {
488 edges[i] = edges[0];
489 base[i] = base[0];
490 int size = GR_CT_MIN(count - i, maxEdges);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000491 target->setVertexSourceToArray(layout, &base[i], size);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000492 drawState->setEdgeAAData(&edges[i], size);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000493 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000494 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000495 }
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000496 drawState->setEdgeAAData(NULL, 0);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000497 } else {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000498 target->setVertexSourceToArray(layout, base, count);
499 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000500 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000501 return true;
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000502 }
503
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000504 if (antiAlias) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000505 // Run the tesselator once to get the boundaries.
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000506 GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fill));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000507 btess.addVertices(base, subpathVertCount, subpathCnt);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000508
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000509 GrMatrix inverse, matrix = drawState->getViewMatrix();
510 if (!drawState->getViewInverse(&inverse)) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000511 return false;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000512 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000513
514 if (btess.vertices().count() > USHRT_MAX) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000515 return false;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000516 }
517
518 // Inflate the boundary, and run the tesselator again to generate
519 // interior polys.
520 const GrPointArray& contourPoints = btess.contourPoints();
521 const GrIndexArray& contours = btess.contours();
522 GrEdgePolygonTess ptess(contourPoints.count(), GLU_TESS_WINDING_NONZERO, matrix);
523
524 size_t i = 0;
525 Sk_gluTessBeginPolygon(ptess.tess(), &ptess);
526 for (int contour = 0; contour < contours.count(); ++contour) {
527 int count = contours[contour];
528 GrEdgeArray edges;
529 int newCount = computeEdgesAndIntersect(matrix, inverse, &btess.contourPoints()[i], count, &edges, 1.0f);
530 Sk_gluTessBeginContour(ptess.tess());
531 for (int j = 0; j < newCount; j++) {
532 ptess.addVertex(contourPoints[i + j], ptess.vertices().count());
533 }
534 i += count;
535 Sk_gluTessEndContour(ptess.tess());
536 }
537
538 Sk_gluTessEndPolygon(ptess.tess());
539
540 if (ptess.vertices().count() > USHRT_MAX) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000541 return false;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000542 }
543
544 // Draw the resulting polys and upload their edge data.
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000545 drawState->enableState(GrDrawState::kEdgeAAConcave_StateBit);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000546 const GrPointArray& vertices = ptess.vertices();
547 const GrIndexArray& indices = ptess.indices();
tomhudson@google.com93813632011-10-27 20:21:16 +0000548 const GrDrawState::Edge* edges = ptess.edges();
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000549 GR_DEBUGASSERT(indices.count() % 3 == 0);
550 for (int i = 0; i < indices.count(); i += 3) {
551 GrPoint tri_verts[3];
552 int index0 = indices[i];
553 int index1 = indices[i + 1];
554 int index2 = indices[i + 2];
555 tri_verts[0] = vertices[index0];
556 tri_verts[1] = vertices[index1];
557 tri_verts[2] = vertices[index2];
tomhudson@google.com93813632011-10-27 20:21:16 +0000558 GrDrawState::Edge tri_edges[6];
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000559 int t = 0;
tomhudson@google.com93813632011-10-27 20:21:16 +0000560 const GrDrawState::Edge& edge0 = edges[index0 * 2];
561 const GrDrawState::Edge& edge1 = edges[index0 * 2 + 1];
562 const GrDrawState::Edge& edge2 = edges[index1 * 2];
563 const GrDrawState::Edge& edge3 = edges[index1 * 2 + 1];
564 const GrDrawState::Edge& edge4 = edges[index2 * 2];
565 const GrDrawState::Edge& edge5 = edges[index2 * 2 + 1];
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000566 if (validEdge(edge0) && validEdge(edge1)) {
567 tri_edges[t++] = edge0;
568 tri_edges[t++] = edge1;
569 }
570 if (validEdge(edge2) && validEdge(edge3)) {
571 tri_edges[t++] = edge2;
572 tri_edges[t++] = edge3;
573 }
574 if (validEdge(edge4) && validEdge(edge5)) {
575 tri_edges[t++] = edge4;
576 tri_edges[t++] = edge5;
577 }
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000578 drawState->setEdgeAAData(&tri_edges[0], t);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000579 target->setVertexSourceToArray(layout, &tri_verts[0], 3);
580 target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000581 }
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000582 drawState->setEdgeAAData(NULL, 0);
583 drawState->disableState(GrDrawState::kEdgeAAConcave_StateBit);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000584 return true;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000585 }
586
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000587 GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fill));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000588 ptess.addVertices(base, subpathVertCount, subpathCnt);
589 const GrPointArray& vertices = ptess.vertices();
590 const GrIndexArray& indices = ptess.indices();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000591 if (indices.count() > 0) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000592 target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
593 target->setIndexSourceToArray(indices.begin(), indices.count());
594 target->drawIndexed(kTriangles_PrimitiveType,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000595 0,
596 0,
597 vertices.count(),
598 indices.count());
599 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000600 return true;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000601}
602
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000603bool GrTesselatedPathRenderer::canDrawPath(const SkPath& path,
bsalomon@google.com289533a2011-10-27 12:34:25 +0000604 GrPathFill fill,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000605 const GrDrawTarget* target,
bsalomon@google.com289533a2011-10-27 12:34:25 +0000606 bool antiAlias) const {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000607 return kHairLine_PathFill != fill;
608}
609