blob: 5920ae143bcdc5b95655433cb13725cb15bdc2f4 [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;
robertphillips@google.com7460b372012-04-25 16:54:51 +000046 r.fX = SkScalarPin(pt.fX,
47 SkFloatToScalar(-kMaxVertexValue),
48 SkFloatToScalar(kMaxVertexValue));
49 r.fY = SkScalarPin(pt.fY,
50 SkFloatToScalar(-kMaxVertexValue),
51 SkFloatToScalar(kMaxVertexValue));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000052 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 }
djsollen@google.com60abb072012-02-15 18:49:15 +000068 virtual ~GrTess() {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +000069 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 }
tomhudson@google.com93813632011-10-27 20:21:16 +0000174 const GrDrawState::Edge* edges() const { return fEdges; }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000175private:
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;
tomhudson@google.com93813632011-10-27 20:21:16 +0000184 GrDrawState::Edge edge = computeEdge(p, q, 1.0f);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000185 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;
tomhudson@google.com93813632011-10-27 20:21:16 +0000191 fEdges = new GrDrawState::Edge[count];
192 memset(fEdges, 0, count * sizeof(GrDrawState::Edge));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000193 }
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;
tomhudson@google.com93813632011-10-27 20:21:16 +0000224 GrDrawState::Edge* fEdges;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000225};
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
tomhudson@google.com93813632011-10-27 20:21:16 +0000269static bool parallel(const GrDrawState::Edge& a, const GrDrawState::Edge& b) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000270 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
tomhudson@google.com93813632011-10-27 20:21:16 +0000306static bool validEdge(const GrDrawState::Edge& edge) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000307 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 }
tomhudson@google.com93813632011-10-27 20:21:16 +0000329 GrDrawState::Edge edge = computeEdge(p, q, sign);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000330 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 }
tomhudson@google.com93813632011-10-27 20:21:16 +0000338 GrDrawState::Edge prev_edge = edges->at(0);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000339 for (int i = 0; i < count; ++i) {
tomhudson@google.com93813632011-10-27 20:21:16 +0000340 GrDrawState::Edge edge = edges->at(i < count - 1 ? i + 1 : 0);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000341 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
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000354bool GrTesselatedPathRenderer::onDrawPath(const SkPath& path,
355 GrPathFill fill,
356 const GrVec* translate,
357 GrDrawTarget* target,
358 GrDrawState::StageMask stageMask,
359 bool antiAlias) {
360
bsalomon@google.com873ea0c2012-03-30 15:55:32 +0000361 GrDrawTarget::AutoStateRestore asr(target, GrDrawTarget::kPreserve_ASRInit);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000362 GrDrawState* drawState = target->drawState();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000363 // face culling doesn't make sense here
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000364 GrAssert(GrDrawState::kBoth_DrawFace == drawState->getDrawFace());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000365
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000366 GrMatrix viewM = drawState->getViewMatrix();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000367
bsalomon@google.com181e9bd2011-09-07 18:42:30 +0000368 GrScalar tol = GR_Scalar1;
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000369 tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, path.getBounds());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000370 GrScalar tolSqd = GrMul(tol, tol);
371
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000372 int subpathCnt;
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000373 int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000374
375 GrVertexLayout layout = 0;
tomhudson@google.com93813632011-10-27 20:21:16 +0000376 for (int s = 0; s < GrDrawState::kNumStages; ++s) {
bsalomon@google.com39ee0ff2011-12-06 15:32:52 +0000377 if ((1 << s) & stageMask) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000378 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
379 }
380 }
381
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000382 bool inverted = GrIsFillInverted(fill);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000383 if (inverted) {
384 maxPts += 4;
385 subpathCnt++;
386 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000387 if (maxPts > USHRT_MAX) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000388 return false;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000389 }
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000390 SkAutoSTMalloc<8, GrPoint> baseMem(maxPts);
391 GrPoint* base = baseMem;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000392 GrPoint* vert = base;
393 GrPoint* subpathBase = base;
394
bsalomon@google.com3582bf92011-06-30 21:32:31 +0000395 SkAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000396
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000397 GrPoint pts[4];
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000398 SkPath::Iter iter(path, false);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000399
400 bool first = true;
401 int subpath = 0;
402
403 for (;;) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000404 switch (iter.next(pts)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000405 case kMove_PathCmd:
406 if (!first) {
407 subpathVertCount[subpath] = vert-subpathBase;
408 subpathBase = vert;
409 ++subpath;
410 }
411 *vert = pts[0];
412 vert++;
413 break;
414 case kLine_PathCmd:
415 *vert = pts[1];
416 vert++;
417 break;
418 case kQuadratic_PathCmd: {
419 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
420 tolSqd, &vert,
421 GrPathUtils::quadraticPointCount(pts, tol));
422 break;
423 }
424 case kCubic_PathCmd: {
425 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
426 tolSqd, &vert,
427 GrPathUtils::cubicPointCount(pts, tol));
428 break;
429 }
430 case kClose_PathCmd:
431 break;
432 case kEnd_PathCmd:
433 subpathVertCount[subpath] = vert-subpathBase;
434 ++subpath; // this could be only in debug
435 goto FINISHED;
436 }
437 first = false;
438 }
439FINISHED:
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000440 if (NULL != translate && 0 != translate->fX && 0 != translate->fY) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000441 for (int i = 0; i < vert - base; i++) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000442 base[i].offset(translate->fX, translate->fY);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000443 }
444 }
445
446 if (inverted) {
447 GrRect bounds;
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000448 GrAssert(NULL != drawState->getRenderTarget());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000449 bounds.setLTRB(0, 0,
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000450 GrIntToScalar(drawState->getRenderTarget()->width()),
451 GrIntToScalar(drawState->getRenderTarget()->height()));
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000452 GrMatrix vmi;
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000453 if (drawState->getViewInverse(&vmi)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000454 vmi.mapRect(&bounds);
455 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000456 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
457 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
458 *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
459 *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000460 subpathVertCount[subpath++] = 4;
461 }
462
463 GrAssert(subpath == subpathCnt);
464 GrAssert((vert - base) <= maxPts);
465
466 size_t count = vert - base;
467
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000468 if (count < 3) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000469 return true;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000470 }
471
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000472 if (subpathCnt == 1 && !inverted && path.isConvex()) {
473 if (antiAlias) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000474 GrEdgeArray edges;
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000475 GrMatrix inverse, matrix = drawState->getViewMatrix();
476 drawState->getViewInverse(&inverse);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000477
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000478 count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000479 size_t maxEdges = target->getMaxEdges();
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000480 if (count == 0) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000481 return true;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000482 }
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000483 if (count <= maxEdges) {
484 // All edges fit; upload all edges and draw all verts as a fan
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000485 target->setVertexSourceToArray(layout, base, count);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000486 drawState->setEdgeAAData(&edges[0], count);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000487 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000488 } else {
489 // Upload "maxEdges" edges and verts at a time, and draw as
490 // separate fans
491 for (size_t i = 0; i < count - 2; i += maxEdges - 2) {
492 edges[i] = edges[0];
493 base[i] = base[0];
494 int size = GR_CT_MIN(count - i, maxEdges);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000495 target->setVertexSourceToArray(layout, &base[i], size);
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000496 drawState->setEdgeAAData(&edges[i], size);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000497 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000498 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000499 }
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000500 drawState->setEdgeAAData(NULL, 0);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000501 } else {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000502 target->setVertexSourceToArray(layout, base, count);
503 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000504 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000505 return true;
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000506 }
507
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000508 if (antiAlias) {
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000509 // Run the tesselator once to get the boundaries.
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000510 GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fill));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000511 btess.addVertices(base, subpathVertCount, subpathCnt);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000512
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000513 GrMatrix inverse, matrix = drawState->getViewMatrix();
514 if (!drawState->getViewInverse(&inverse)) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000515 return false;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000516 }
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000517
518 if (btess.vertices().count() > USHRT_MAX) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000519 return false;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000520 }
521
522 // Inflate the boundary, and run the tesselator again to generate
523 // interior polys.
524 const GrPointArray& contourPoints = btess.contourPoints();
525 const GrIndexArray& contours = btess.contours();
526 GrEdgePolygonTess ptess(contourPoints.count(), GLU_TESS_WINDING_NONZERO, matrix);
527
528 size_t i = 0;
529 Sk_gluTessBeginPolygon(ptess.tess(), &ptess);
530 for (int contour = 0; contour < contours.count(); ++contour) {
531 int count = contours[contour];
532 GrEdgeArray edges;
533 int newCount = computeEdgesAndIntersect(matrix, inverse, &btess.contourPoints()[i], count, &edges, 1.0f);
534 Sk_gluTessBeginContour(ptess.tess());
535 for (int j = 0; j < newCount; j++) {
536 ptess.addVertex(contourPoints[i + j], ptess.vertices().count());
537 }
538 i += count;
539 Sk_gluTessEndContour(ptess.tess());
540 }
541
542 Sk_gluTessEndPolygon(ptess.tess());
543
544 if (ptess.vertices().count() > USHRT_MAX) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000545 return false;
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000546 }
547
548 // Draw the resulting polys and upload their edge data.
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000549 drawState->enableState(GrDrawState::kEdgeAAConcave_StateBit);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000550 const GrPointArray& vertices = ptess.vertices();
551 const GrIndexArray& indices = ptess.indices();
tomhudson@google.com93813632011-10-27 20:21:16 +0000552 const GrDrawState::Edge* edges = ptess.edges();
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000553 GR_DEBUGASSERT(indices.count() % 3 == 0);
554 for (int i = 0; i < indices.count(); i += 3) {
555 GrPoint tri_verts[3];
556 int index0 = indices[i];
557 int index1 = indices[i + 1];
558 int index2 = indices[i + 2];
559 tri_verts[0] = vertices[index0];
560 tri_verts[1] = vertices[index1];
561 tri_verts[2] = vertices[index2];
tomhudson@google.com93813632011-10-27 20:21:16 +0000562 GrDrawState::Edge tri_edges[6];
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000563 int t = 0;
tomhudson@google.com93813632011-10-27 20:21:16 +0000564 const GrDrawState::Edge& edge0 = edges[index0 * 2];
565 const GrDrawState::Edge& edge1 = edges[index0 * 2 + 1];
566 const GrDrawState::Edge& edge2 = edges[index1 * 2];
567 const GrDrawState::Edge& edge3 = edges[index1 * 2 + 1];
568 const GrDrawState::Edge& edge4 = edges[index2 * 2];
569 const GrDrawState::Edge& edge5 = edges[index2 * 2 + 1];
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000570 if (validEdge(edge0) && validEdge(edge1)) {
571 tri_edges[t++] = edge0;
572 tri_edges[t++] = edge1;
573 }
574 if (validEdge(edge2) && validEdge(edge3)) {
575 tri_edges[t++] = edge2;
576 tri_edges[t++] = edge3;
577 }
578 if (validEdge(edge4) && validEdge(edge5)) {
579 tri_edges[t++] = edge4;
580 tri_edges[t++] = edge5;
581 }
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000582 drawState->setEdgeAAData(&tri_edges[0], t);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000583 target->setVertexSourceToArray(layout, &tri_verts[0], 3);
584 target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000585 }
bsalomon@google.com8f9cbd62011-12-09 15:55:34 +0000586 drawState->setEdgeAAData(NULL, 0);
587 drawState->disableState(GrDrawState::kEdgeAAConcave_StateBit);
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000588 return true;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000589 }
590
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000591 GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fill));
senorblanco@chromium.org129b8e32011-06-15 17:52:09 +0000592 ptess.addVertices(base, subpathVertCount, subpathCnt);
593 const GrPointArray& vertices = ptess.vertices();
594 const GrIndexArray& indices = ptess.indices();
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000595 if (indices.count() > 0) {
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000596 target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
597 target->setIndexSourceToArray(indices.begin(), indices.count());
598 target->drawIndexed(kTriangles_PrimitiveType,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000599 0,
600 0,
601 vertices.count(),
602 indices.count());
603 }
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000604 return true;
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000605}
606
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000607bool GrTesselatedPathRenderer::canDrawPath(const SkPath& path,
bsalomon@google.com289533a2011-10-27 12:34:25 +0000608 GrPathFill fill,
bsalomon@google.comc2099d22012-03-02 21:26:50 +0000609 const GrDrawTarget* target,
bsalomon@google.com289533a2011-10-27 12:34:25 +0000610 bool antiAlias) const {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000611 return kHairLine_PathFill != fill;
612}
613