blob: 8a33012872f44d3c010b694a73c9c747e462f7d9 [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"
reed@google.com07f3ee12011-05-16 17:21:57 +000021#include "GrPoint.h"
22#include "GrTDArray.h"
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000023
24#include <internal_glu.h>
25
26struct PolygonData {
27 PolygonData(GrTDArray<GrPoint>* vertices, GrTDArray<short>* indices)
28 : fVertices(vertices)
29 , fIndices(indices)
30 {
31 }
32 GrTDArray<GrPoint>* fVertices;
33 GrTDArray<short>* fIndices;
34};
35
36static void beginData(GLenum type, void* data)
37{
38 GR_DEBUGASSERT(type == GL_TRIANGLES);
39}
40
41static void edgeFlagData(GLboolean flag, void* data)
42{
43}
44
45static void vertexData(void* vertexData, void* data)
46{
47 short* end = static_cast<PolygonData*>(data)->fIndices->append();
48 *end = reinterpret_cast<long>(vertexData);
49}
50
51static void endData(void* data)
52{
53}
54
55static void combineData(GLdouble coords[3], void* vertexData[4],
56 GLfloat weight[4], void **outData, void* data)
57{
58 PolygonData* polygonData = static_cast<PolygonData*>(data);
59 int index = polygonData->fVertices->count();
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +000060 *polygonData->fVertices->append() = GrPoint::Make(static_cast<float>(coords[0]),
61 static_cast<float>(coords[1]));
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +000062 *outData = reinterpret_cast<void*>(index);
63}
64
65typedef void (*TESSCB)();
66
67static unsigned fill_type_to_glu_winding_rule(GrPathFill fill) {
68 switch (fill) {
69 case kWinding_PathFill:
70 return GLU_TESS_WINDING_NONZERO;
71 case kEvenOdd_PathFill:
72 return GLU_TESS_WINDING_ODD;
73 case kInverseWinding_PathFill:
74 return GLU_TESS_WINDING_POSITIVE;
75 case kInverseEvenOdd_PathFill:
76 return GLU_TESS_WINDING_ODD;
77 case kHairLine_PathFill:
78 return GLU_TESS_WINDING_NONZERO; // FIXME: handle this
79 default:
80 GrAssert(!"Unknown path fill!");
81 return 0;
82 }
83}
84
85GrTesselatedPathRenderer::GrTesselatedPathRenderer() {
86}
87
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +000088typedef GrTDArray<GrDrawTarget::Edge> EdgeArray;
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +000089
senorblanco@chromium.orgff174b32011-05-16 16:59:57 +000090bool isCCW(const GrPoint* pts)
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +000091{
senorblanco@chromium.orgff174b32011-05-16 16:59:57 +000092 GrVec v1 = pts[1] - pts[0];
93 GrVec v2 = pts[2] - pts[1];
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +000094 return v1.cross(v2) < 0;
95}
96
97static size_t computeEdgesAndOffsetVertices(const GrMatrix& matrix,
98 const GrMatrix& inverse,
99 GrPoint* vertices,
100 size_t numVertices,
101 EdgeArray* edges)
102{
senorblanco@chromium.orgff174b32011-05-16 16:59:57 +0000103 matrix.mapPoints(vertices, numVertices);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000104 GrPoint p = vertices[numVertices - 1];
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000105 float sign = isCCW(vertices) ? -1.0f : 1.0f;
106 for (size_t i = 0; i < numVertices; ++i) {
107 GrPoint q = vertices[i];
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000108 if (p == q) continue;
109 GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
110 float scale = sign / tangent.length();
111 float cross2 = p.fX * q.fY - q.fX * p.fY;
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000112 GrDrawTarget::Edge edge(tangent.fX * scale,
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000113 tangent.fY * scale,
114 cross2 * scale + 0.5f);
115 *edges->append() = edge;
116 p = q;
117 }
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000118 GrDrawTarget::Edge prev_edge = *edges->back();
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000119 for (size_t i = 0; i < edges->count(); ++i) {
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000120 GrDrawTarget::Edge edge = edges->at(i);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000121 vertices[i] = prev_edge.intersect(edge);
122 inverse.mapPoints(&vertices[i], 1);
123 prev_edge = edge;
124 }
125 return edges->count();
126}
127
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000128void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
129 GrDrawTarget::StageBitfield stages,
reed@google.com07f3ee12011-05-16 17:21:57 +0000130 const GrPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000131 GrPathFill fill,
132 const GrPoint* translate) {
133 GrDrawTarget::AutoStateRestore asr(target);
134 bool colorWritesWereDisabled = target->isColorWriteDisabled();
135 // face culling doesn't make sense here
136 GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
137
138 GrMatrix viewM = target->getViewMatrix();
139 // In order to tesselate the path we get a bound on how much the matrix can
140 // stretch when mapping to screen coordinates.
141 GrScalar stretch = viewM.getMaxStretch();
142 bool useStretch = stretch > 0;
143 GrScalar tol = GrPathUtils::gTolerance;
144
145 if (!useStretch) {
146 // TODO: deal with perspective in some better way.
147 tol /= 10;
148 } else {
149 GrScalar sinv = GR_Scalar1 / stretch;
150 tol = GrMul(tol, sinv);
151 }
152 GrScalar tolSqd = GrMul(tol, tol);
153
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000154 int subpathCnt;
155 int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
156
157 GrVertexLayout layout = 0;
158 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
159 if ((1 << s) & stages) {
160 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
161 }
162 }
163
164 bool inverted = IsFillInverted(fill);
165 if (inverted) {
166 maxPts += 4;
167 subpathCnt++;
168 }
169 GrPoint* base = new GrPoint[maxPts];
170 GrPoint* vert = base;
171 GrPoint* subpathBase = base;
172
173 GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
174
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000175 GrPoint pts[4];
reed@google.com07f3ee12011-05-16 17:21:57 +0000176 SkPath::Iter iter(path, true);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000177
178 bool first = true;
179 int subpath = 0;
180
181 for (;;) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000182 switch (iter.next(pts)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000183 case kMove_PathCmd:
184 if (!first) {
185 subpathVertCount[subpath] = vert-subpathBase;
186 subpathBase = vert;
187 ++subpath;
188 }
189 *vert = pts[0];
190 vert++;
191 break;
192 case kLine_PathCmd:
193 *vert = pts[1];
194 vert++;
195 break;
196 case kQuadratic_PathCmd: {
197 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
198 tolSqd, &vert,
199 GrPathUtils::quadraticPointCount(pts, tol));
200 break;
201 }
202 case kCubic_PathCmd: {
203 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
204 tolSqd, &vert,
205 GrPathUtils::cubicPointCount(pts, tol));
206 break;
207 }
208 case kClose_PathCmd:
209 break;
210 case kEnd_PathCmd:
211 subpathVertCount[subpath] = vert-subpathBase;
212 ++subpath; // this could be only in debug
213 goto FINISHED;
214 }
215 first = false;
216 }
217FINISHED:
218 if (translate) {
219 for (int i = 0; i < vert - base; i++) {
220 base[i].offset(translate->fX, translate->fY);
221 }
222 }
223
224 if (inverted) {
225 GrRect bounds;
226 GrAssert(NULL != target->getRenderTarget());
227 bounds.setLTRB(0, 0,
228 GrIntToScalar(target->getRenderTarget()->width()),
229 GrIntToScalar(target->getRenderTarget()->height()));
230 GrMatrix vmi;
231 if (target->getViewInverse(&vmi)) {
232 vmi.mapRect(&bounds);
233 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000234 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
235 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
236 *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
237 *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000238 subpathVertCount[subpath++] = 4;
239 }
240
241 GrAssert(subpath == subpathCnt);
242 GrAssert((vert - base) <= maxPts);
243
244 size_t count = vert - base;
245
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000246 if (count < 3) {
247 delete[] base;
248 return;
249 }
250
reed@google.com07f3ee12011-05-16 17:21:57 +0000251 if (subpathCnt == 1 && !inverted && path.isConvex()) {
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000252 if (target->isAntialiasState()) {
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000253 EdgeArray edges;
254 GrMatrix inverse, matrix = target->getViewMatrix();
255 target->getViewInverse(&inverse);
256
257 count = computeEdgesAndOffsetVertices(matrix, inverse, base, count, &edges);
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000258 int maxEdges = target->getMaxEdges();
259 if (count <= maxEdges) {
260 // All edges fit; upload all edges and draw all verts as a fan
261 target->setVertexSourceToArray(layout, base, count);
262 target->setEdgeAAData(&edges[0], count);
263 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
264 } else {
265 // Upload "maxEdges" edges and verts at a time, and draw as
266 // separate fans
267 for (size_t i = 0; i < count - 2; i += maxEdges - 2) {
268 edges[i] = edges[0];
269 base[i] = base[0];
270 int size = GR_CT_MIN(count - i, maxEdges);
271 target->setVertexSourceToArray(layout, &base[i], size);
272 target->setEdgeAAData(&edges[i], size);
273 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
274 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000275 }
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000276 target->setEdgeAAData(NULL, 0);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000277 } else {
278 target->setVertexSourceToArray(layout, base, count);
279 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
280 }
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000281 delete[] base;
282 return;
283 }
284
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000285 // FIXME: This copy could be removed if we had (templated?) versions of
286 // generate_*_point above that wrote directly into doubles.
287 double* inVertices = new double[count * 3];
288 for (size_t i = 0; i < count; ++i) {
289 inVertices[i * 3] = base[i].fX;
290 inVertices[i * 3 + 1] = base[i].fY;
291 inVertices[i * 3 + 2] = 1.0;
292 }
293
294 GLUtesselator* tess = internal_gluNewTess();
295 unsigned windingRule = fill_type_to_glu_winding_rule(fill);
296 internal_gluTessProperty(tess, GLU_TESS_WINDING_RULE, windingRule);
297 internal_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData);
298 internal_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData);
299 internal_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData);
300 internal_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData);
301 internal_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData);
302 GrTDArray<short> indices;
303 GrTDArray<GrPoint> vertices;
304 PolygonData data(&vertices, &indices);
305
306 internal_gluTessBeginPolygon(tess, &data);
307 size_t i = 0;
308 for (int sp = 0; sp < subpathCnt; ++sp) {
309 internal_gluTessBeginContour(tess);
310 int start = i;
311 int end = start + subpathVertCount[sp];
312 for (; i < end; ++i) {
313 double* inVertex = &inVertices[i * 3];
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000314 *vertices.append() = GrPoint::Make(inVertex[0], inVertex[1]);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000315 internal_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i));
316 }
317 internal_gluTessEndContour(tess);
318 }
319
320 internal_gluTessEndPolygon(tess);
321 internal_gluDeleteTess(tess);
322
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000323 if (indices.count() > 0) {
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000324 target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
325 target->setIndexSourceToArray(indices.begin(), indices.count());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000326 target->drawIndexed(kTriangles_PrimitiveType,
327 0,
328 0,
329 vertices.count(),
330 indices.count());
331 }
332 delete[] inVertices;
333 delete[] base;
334}
335
336bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000337 const SkPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000338 GrPathFill fill) const {
339 return kHairLine_PathFill != fill;
340}
341
342void GrTesselatedPathRenderer::drawPathToStencil(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000343 const SkPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000344 GrPathFill fill,
345 const GrPoint* translate) {
346 GrAlwaysAssert(!"multipass stencil should not be needed");
347}
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000348
349bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000350 const SkPath& path,
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000351 GrPathFill fill) {
352 int subpathCnt = 0;
353 int tol = GrPathUtils::gTolerance;
354 GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
355 return (subpathCnt == 1 &&
356 !IsFillInverted(fill) &&
reed@google.com07f3ee12011-05-16 17:21:57 +0000357 path.isConvex());
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000358}