blob: 3993adb5d92a6a25c7d7ea4806ee4c5d04689ac1 [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
100bool isCCW(const GrPoint* v)
101{
102 GrVec v1 = v[1] - v[0];
103 GrVec v2 = v[2] - v[1];
104 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{
113 GrPoint p = vertices[numVertices - 1];
114 matrix.mapPoints(&p, 1);
115 float sign = isCCW(vertices) ? -1.0f : 1.0f;
116 for (size_t i = 0; i < numVertices; ++i) {
117 GrPoint q = vertices[i];
118 matrix.mapPoints(&q, 1);
119 if (p == q) continue;
120 GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
121 float scale = sign / tangent.length();
122 float cross2 = p.fX * q.fY - q.fX * p.fY;
123 Edge edge(tangent.fX * scale,
124 tangent.fY * scale,
125 cross2 * scale + 0.5f);
126 *edges->append() = edge;
127 p = q;
128 }
129 Edge prev_edge = *edges->back();
130 for (size_t i = 0; i < edges->count(); ++i) {
131 Edge edge = edges->at(i);
132 vertices[i] = prev_edge.intersect(edge);
133 inverse.mapPoints(&vertices[i], 1);
134 prev_edge = edge;
135 }
136 return edges->count();
137}
138
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000139void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
140 GrDrawTarget::StageBitfield stages,
141 GrPathIter* path,
142 GrPathFill fill,
143 const GrPoint* translate) {
144 GrDrawTarget::AutoStateRestore asr(target);
145 bool colorWritesWereDisabled = target->isColorWriteDisabled();
146 // face culling doesn't make sense here
147 GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
148
149 GrMatrix viewM = target->getViewMatrix();
150 // In order to tesselate the path we get a bound on how much the matrix can
151 // stretch when mapping to screen coordinates.
152 GrScalar stretch = viewM.getMaxStretch();
153 bool useStretch = stretch > 0;
154 GrScalar tol = GrPathUtils::gTolerance;
155
156 if (!useStretch) {
157 // TODO: deal with perspective in some better way.
158 tol /= 10;
159 } else {
160 GrScalar sinv = GR_Scalar1 / stretch;
161 tol = GrMul(tol, sinv);
162 }
163 GrScalar tolSqd = GrMul(tol, tol);
164
165 path->rewind();
166
167 int subpathCnt;
168 int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
169
170 GrVertexLayout layout = 0;
171 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
172 if ((1 << s) & stages) {
173 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
174 }
175 }
176
177 bool inverted = IsFillInverted(fill);
178 if (inverted) {
179 maxPts += 4;
180 subpathCnt++;
181 }
182 GrPoint* base = new GrPoint[maxPts];
183 GrPoint* vert = base;
184 GrPoint* subpathBase = base;
185
186 GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
187
188 path->rewind();
189
190 GrPoint pts[4];
191
192 bool first = true;
193 int subpath = 0;
194
195 for (;;) {
196 GrPathCmd cmd = path->next(pts);
197 switch (cmd) {
198 case kMove_PathCmd:
199 if (!first) {
200 subpathVertCount[subpath] = vert-subpathBase;
201 subpathBase = vert;
202 ++subpath;
203 }
204 *vert = pts[0];
205 vert++;
206 break;
207 case kLine_PathCmd:
208 *vert = pts[1];
209 vert++;
210 break;
211 case kQuadratic_PathCmd: {
212 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
213 tolSqd, &vert,
214 GrPathUtils::quadraticPointCount(pts, tol));
215 break;
216 }
217 case kCubic_PathCmd: {
218 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
219 tolSqd, &vert,
220 GrPathUtils::cubicPointCount(pts, tol));
221 break;
222 }
223 case kClose_PathCmd:
224 break;
225 case kEnd_PathCmd:
226 subpathVertCount[subpath] = vert-subpathBase;
227 ++subpath; // this could be only in debug
228 goto FINISHED;
229 }
230 first = false;
231 }
232FINISHED:
233 if (translate) {
234 for (int i = 0; i < vert - base; i++) {
235 base[i].offset(translate->fX, translate->fY);
236 }
237 }
238
239 if (inverted) {
240 GrRect bounds;
241 GrAssert(NULL != target->getRenderTarget());
242 bounds.setLTRB(0, 0,
243 GrIntToScalar(target->getRenderTarget()->width()),
244 GrIntToScalar(target->getRenderTarget()->height()));
245 GrMatrix vmi;
246 if (target->getViewInverse(&vmi)) {
247 vmi.mapRect(&bounds);
248 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000249 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
250 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
251 *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
252 *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000253 subpathVertCount[subpath++] = 4;
254 }
255
256 GrAssert(subpath == subpathCnt);
257 GrAssert((vert - base) <= maxPts);
258
259 size_t count = vert - base;
260
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000261 if (count < 3) {
262 delete[] base;
263 return;
264 }
265
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000266 if (subpathCnt == 1 && !inverted && path->convexHint() == kConvex_ConvexHint) {
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000267 if (target->isAntialiasState()) {
268 target->enableState(GrDrawTarget::kEdgeAA_StateBit);
269 EdgeArray edges;
270 GrMatrix inverse, matrix = target->getViewMatrix();
271 target->getViewInverse(&inverse);
272
273 count = computeEdgesAndOffsetVertices(matrix, inverse, base, count, &edges);
274 GrPoint triangle[3];
275 triangle[0] = base[0];
276 Edge triangleEdges[6];
277 triangleEdges[0] = *edges.back();
278 triangleEdges[1] = edges[0];
279 for (size_t i = 1; i < count - 1; i++) {
280 triangle[1] = base[i];
281 triangle[2] = base[i + 1];
282 triangleEdges[2] = edges[i - 1];
283 triangleEdges[3] = edges[i];
284 triangleEdges[4] = edges[i];
285 triangleEdges[5] = edges[i + 1];
286 target->setVertexSourceToArray(layout, triangle, 3);
287 target->setEdgeAAData(&triangleEdges[0].fX);
288 target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
289 }
290 target->disableState(GrDrawTarget::kEdgeAA_StateBit);
291 } else {
292 target->setVertexSourceToArray(layout, base, count);
293 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
294 }
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000295 delete[] base;
296 return;
297 }
298
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000299 // FIXME: This copy could be removed if we had (templated?) versions of
300 // generate_*_point above that wrote directly into doubles.
301 double* inVertices = new double[count * 3];
302 for (size_t i = 0; i < count; ++i) {
303 inVertices[i * 3] = base[i].fX;
304 inVertices[i * 3 + 1] = base[i].fY;
305 inVertices[i * 3 + 2] = 1.0;
306 }
307
308 GLUtesselator* tess = internal_gluNewTess();
309 unsigned windingRule = fill_type_to_glu_winding_rule(fill);
310 internal_gluTessProperty(tess, GLU_TESS_WINDING_RULE, windingRule);
311 internal_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData);
312 internal_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData);
313 internal_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData);
314 internal_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData);
315 internal_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData);
316 GrTDArray<short> indices;
317 GrTDArray<GrPoint> vertices;
318 PolygonData data(&vertices, &indices);
319
320 internal_gluTessBeginPolygon(tess, &data);
321 size_t i = 0;
322 for (int sp = 0; sp < subpathCnt; ++sp) {
323 internal_gluTessBeginContour(tess);
324 int start = i;
325 int end = start + subpathVertCount[sp];
326 for (; i < end; ++i) {
327 double* inVertex = &inVertices[i * 3];
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000328 *vertices.append() = GrPoint::Make(inVertex[0], inVertex[1]);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000329 internal_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i));
330 }
331 internal_gluTessEndContour(tess);
332 }
333
334 internal_gluTessEndPolygon(tess);
335 internal_gluDeleteTess(tess);
336
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000337 if (indices.count() > 0) {
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000338 target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
339 target->setIndexSourceToArray(indices.begin(), indices.count());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000340 target->drawIndexed(kTriangles_PrimitiveType,
341 0,
342 0,
343 vertices.count(),
344 indices.count());
345 }
346 delete[] inVertices;
347 delete[] base;
348}
349
350bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
351 GrPathIter* path,
352 GrPathFill fill) const {
353 return kHairLine_PathFill != fill;
354}
355
356void GrTesselatedPathRenderer::drawPathToStencil(GrDrawTarget* target,
357 GrPathIter* path,
358 GrPathFill fill,
359 const GrPoint* translate) {
360 GrAlwaysAssert(!"multipass stencil should not be needed");
361}
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000362
363bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
364 GrPathIter* path,
365 GrPathFill fill) {
366 int subpathCnt = 0;
367 int tol = GrPathUtils::gTolerance;
368 GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
369 return (subpathCnt == 1 &&
370 !IsFillInverted(fill) &&
371 path->convexHint() == kConvex_ConvexHint);
372}