blob: 19ee46d3f622aa6d24a6387eb5560c89a81688df [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"
21
22#include <internal_glu.h>
23
24struct PolygonData {
25 PolygonData(GrTDArray<GrPoint>* vertices, GrTDArray<short>* indices)
26 : fVertices(vertices)
27 , fIndices(indices)
28 {
29 }
30 GrTDArray<GrPoint>* fVertices;
31 GrTDArray<short>* fIndices;
32};
33
34static void beginData(GLenum type, void* data)
35{
36 GR_DEBUGASSERT(type == GL_TRIANGLES);
37}
38
39static void edgeFlagData(GLboolean flag, void* data)
40{
41}
42
43static void vertexData(void* vertexData, void* data)
44{
45 short* end = static_cast<PolygonData*>(data)->fIndices->append();
46 *end = reinterpret_cast<long>(vertexData);
47}
48
49static void endData(void* data)
50{
51}
52
53static void combineData(GLdouble coords[3], void* vertexData[4],
54 GLfloat weight[4], void **outData, void* data)
55{
56 PolygonData* polygonData = static_cast<PolygonData*>(data);
57 int index = polygonData->fVertices->count();
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +000058 *polygonData->fVertices->append() = GrPoint::Make(static_cast<float>(coords[0]),
59 static_cast<float>(coords[1]));
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000060 *outData = reinterpret_cast<void*>(index);
61}
62
63typedef void (*TESSCB)();
64
65static unsigned fill_type_to_glu_winding_rule(GrPathFill fill) {
66 switch (fill) {
67 case kWinding_PathFill:
68 return GLU_TESS_WINDING_NONZERO;
69 case kEvenOdd_PathFill:
70 return GLU_TESS_WINDING_ODD;
71 case kInverseWinding_PathFill:
72 return GLU_TESS_WINDING_POSITIVE;
73 case kInverseEvenOdd_PathFill:
74 return GLU_TESS_WINDING_ODD;
75 case kHairLine_PathFill:
76 return GLU_TESS_WINDING_NONZERO; // FIXME: handle this
77 default:
78 GrAssert(!"Unknown path fill!");
79 return 0;
80 }
81}
82
83GrTesselatedPathRenderer::GrTesselatedPathRenderer() {
84}
85
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +000086class Edge {
87 public:
88 Edge() {}
89 Edge(float x, float y, float z) : fX(x), fY(y), fZ(z) {}
90 GrPoint intersect(const Edge& other) {
91 return GrPoint::Make(
92 (fY * other.fZ - other.fY * fZ) / (fX * other.fY - other.fX * fY),
93 (fX * other.fZ - other.fX * fZ) / (other.fX * fY - fX * other.fY));
94 }
95 float fX, fY, fZ;
96};
97
98typedef GrTDArray<Edge> EdgeArray;
99
senorblanco@chromium.orgff174b32011-05-16 16:59:57 +0000100bool isCCW(const GrPoint* pts)
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000101{
senorblanco@chromium.orgff174b32011-05-16 16:59:57 +0000102 GrVec v1 = pts[1] - pts[0];
103 GrVec v2 = pts[2] - pts[1];
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000104 return v1.cross(v2) < 0;
105}
106
107static size_t computeEdgesAndOffsetVertices(const GrMatrix& matrix,
108 const GrMatrix& inverse,
109 GrPoint* vertices,
110 size_t numVertices,
111 EdgeArray* edges)
112{
senorblanco@chromium.orgff174b32011-05-16 16:59:57 +0000113 matrix.mapPoints(vertices, numVertices);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000114 GrPoint p = vertices[numVertices - 1];
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000115 float sign = isCCW(vertices) ? -1.0f : 1.0f;
116 for (size_t i = 0; i < numVertices; ++i) {
117 GrPoint q = vertices[i];
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000118 if (p == q) continue;
119 GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
120 float scale = sign / tangent.length();
121 float cross2 = p.fX * q.fY - q.fX * p.fY;
122 Edge edge(tangent.fX * scale,
123 tangent.fY * scale,
124 cross2 * scale + 0.5f);
125 *edges->append() = edge;
126 p = q;
127 }
128 Edge prev_edge = *edges->back();
129 for (size_t i = 0; i < edges->count(); ++i) {
130 Edge edge = edges->at(i);
131 vertices[i] = prev_edge.intersect(edge);
132 inverse.mapPoints(&vertices[i], 1);
133 prev_edge = edge;
134 }
135 return edges->count();
136}
137
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000138void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
139 GrDrawTarget::StageBitfield stages,
140 GrPathIter* path,
141 GrPathFill fill,
142 const GrPoint* translate) {
143 GrDrawTarget::AutoStateRestore asr(target);
144 bool colorWritesWereDisabled = target->isColorWriteDisabled();
145 // face culling doesn't make sense here
146 GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
147
148 GrMatrix viewM = target->getViewMatrix();
149 // In order to tesselate the path we get a bound on how much the matrix can
150 // stretch when mapping to screen coordinates.
151 GrScalar stretch = viewM.getMaxStretch();
152 bool useStretch = stretch > 0;
153 GrScalar tol = GrPathUtils::gTolerance;
154
155 if (!useStretch) {
156 // TODO: deal with perspective in some better way.
157 tol /= 10;
158 } else {
159 GrScalar sinv = GR_Scalar1 / stretch;
160 tol = GrMul(tol, sinv);
161 }
162 GrScalar tolSqd = GrMul(tol, tol);
163
164 path->rewind();
165
166 int subpathCnt;
167 int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
168
169 GrVertexLayout layout = 0;
170 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
171 if ((1 << s) & stages) {
172 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
173 }
174 }
175
176 bool inverted = IsFillInverted(fill);
177 if (inverted) {
178 maxPts += 4;
179 subpathCnt++;
180 }
181 GrPoint* base = new GrPoint[maxPts];
182 GrPoint* vert = base;
183 GrPoint* subpathBase = base;
184
185 GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
186
187 path->rewind();
188
189 GrPoint pts[4];
190
191 bool first = true;
192 int subpath = 0;
193
194 for (;;) {
195 GrPathCmd cmd = path->next(pts);
196 switch (cmd) {
197 case kMove_PathCmd:
198 if (!first) {
199 subpathVertCount[subpath] = vert-subpathBase;
200 subpathBase = vert;
201 ++subpath;
202 }
203 *vert = pts[0];
204 vert++;
205 break;
206 case kLine_PathCmd:
207 *vert = pts[1];
208 vert++;
209 break;
210 case kQuadratic_PathCmd: {
211 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
212 tolSqd, &vert,
213 GrPathUtils::quadraticPointCount(pts, tol));
214 break;
215 }
216 case kCubic_PathCmd: {
217 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
218 tolSqd, &vert,
219 GrPathUtils::cubicPointCount(pts, tol));
220 break;
221 }
222 case kClose_PathCmd:
223 break;
224 case kEnd_PathCmd:
225 subpathVertCount[subpath] = vert-subpathBase;
226 ++subpath; // this could be only in debug
227 goto FINISHED;
228 }
229 first = false;
230 }
231FINISHED:
232 if (translate) {
233 for (int i = 0; i < vert - base; i++) {
234 base[i].offset(translate->fX, translate->fY);
235 }
236 }
237
238 if (inverted) {
239 GrRect bounds;
240 GrAssert(NULL != target->getRenderTarget());
241 bounds.setLTRB(0, 0,
242 GrIntToScalar(target->getRenderTarget()->width()),
243 GrIntToScalar(target->getRenderTarget()->height()));
244 GrMatrix vmi;
245 if (target->getViewInverse(&vmi)) {
246 vmi.mapRect(&bounds);
247 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000248 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
249 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
250 *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
251 *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000252 subpathVertCount[subpath++] = 4;
253 }
254
255 GrAssert(subpath == subpathCnt);
256 GrAssert((vert - base) <= maxPts);
257
258 size_t count = vert - base;
259
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000260 if (count < 3) {
261 delete[] base;
262 return;
263 }
264
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000265 if (subpathCnt == 1 && !inverted && path->convexHint() == kConvex_ConvexHint) {
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000266 if (target->isAntialiasState()) {
267 target->enableState(GrDrawTarget::kEdgeAA_StateBit);
268 EdgeArray edges;
269 GrMatrix inverse, matrix = target->getViewMatrix();
270 target->getViewInverse(&inverse);
271
272 count = computeEdgesAndOffsetVertices(matrix, inverse, base, count, &edges);
273 GrPoint triangle[3];
274 triangle[0] = base[0];
275 Edge triangleEdges[6];
276 triangleEdges[0] = *edges.back();
277 triangleEdges[1] = edges[0];
278 for (size_t i = 1; i < count - 1; i++) {
279 triangle[1] = base[i];
280 triangle[2] = base[i + 1];
281 triangleEdges[2] = edges[i - 1];
282 triangleEdges[3] = edges[i];
283 triangleEdges[4] = edges[i];
284 triangleEdges[5] = edges[i + 1];
285 target->setVertexSourceToArray(layout, triangle, 3);
286 target->setEdgeAAData(&triangleEdges[0].fX);
287 target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
288 }
289 target->disableState(GrDrawTarget::kEdgeAA_StateBit);
290 } else {
291 target->setVertexSourceToArray(layout, base, count);
292 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
293 }
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000294 delete[] base;
295 return;
296 }
297
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000298 // FIXME: This copy could be removed if we had (templated?) versions of
299 // generate_*_point above that wrote directly into doubles.
300 double* inVertices = new double[count * 3];
301 for (size_t i = 0; i < count; ++i) {
302 inVertices[i * 3] = base[i].fX;
303 inVertices[i * 3 + 1] = base[i].fY;
304 inVertices[i * 3 + 2] = 1.0;
305 }
306
307 GLUtesselator* tess = internal_gluNewTess();
308 unsigned windingRule = fill_type_to_glu_winding_rule(fill);
309 internal_gluTessProperty(tess, GLU_TESS_WINDING_RULE, windingRule);
310 internal_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData);
311 internal_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData);
312 internal_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData);
313 internal_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData);
314 internal_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData);
315 GrTDArray<short> indices;
316 GrTDArray<GrPoint> vertices;
317 PolygonData data(&vertices, &indices);
318
319 internal_gluTessBeginPolygon(tess, &data);
320 size_t i = 0;
321 for (int sp = 0; sp < subpathCnt; ++sp) {
322 internal_gluTessBeginContour(tess);
323 int start = i;
324 int end = start + subpathVertCount[sp];
325 for (; i < end; ++i) {
326 double* inVertex = &inVertices[i * 3];
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000327 *vertices.append() = GrPoint::Make(inVertex[0], inVertex[1]);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000328 internal_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i));
329 }
330 internal_gluTessEndContour(tess);
331 }
332
333 internal_gluTessEndPolygon(tess);
334 internal_gluDeleteTess(tess);
335
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000336 if (indices.count() > 0) {
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000337 target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
338 target->setIndexSourceToArray(indices.begin(), indices.count());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000339 target->drawIndexed(kTriangles_PrimitiveType,
340 0,
341 0,
342 vertices.count(),
343 indices.count());
344 }
345 delete[] inVertices;
346 delete[] base;
347}
348
349bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
350 GrPathIter* path,
351 GrPathFill fill) const {
352 return kHairLine_PathFill != fill;
353}
354
355void GrTesselatedPathRenderer::drawPathToStencil(GrDrawTarget* target,
356 GrPathIter* path,
357 GrPathFill fill,
358 const GrPoint* translate) {
359 GrAlwaysAssert(!"multipass stencil should not be needed");
360}
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000361
362bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
363 GrPathIter* path,
364 GrPathFill fill) {
365 int subpathCnt = 0;
366 int tol = GrPathUtils::gTolerance;
367 GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
368 return (subpathCnt == 1 &&
369 !IsFillInverted(fill) &&
370 path->convexHint() == kConvex_ConvexHint);
371}