blob: 51d6db8e10b32d089a31e591c3202a9b5934e688 [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.org64cc5792011-05-19 19:58:58 +0000119 for (int 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);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000134 // face culling doesn't make sense here
135 GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
136
137 GrMatrix viewM = target->getViewMatrix();
138 // In order to tesselate the path we get a bound on how much the matrix can
139 // stretch when mapping to screen coordinates.
140 GrScalar stretch = viewM.getMaxStretch();
141 bool useStretch = stretch > 0;
142 GrScalar tol = GrPathUtils::gTolerance;
143
144 if (!useStretch) {
145 // TODO: deal with perspective in some better way.
146 tol /= 10;
147 } else {
148 GrScalar sinv = GR_Scalar1 / stretch;
149 tol = GrMul(tol, sinv);
150 }
151 GrScalar tolSqd = GrMul(tol, tol);
152
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000153 int subpathCnt;
154 int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
155
156 GrVertexLayout layout = 0;
157 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
158 if ((1 << s) & stages) {
159 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
160 }
161 }
162
163 bool inverted = IsFillInverted(fill);
164 if (inverted) {
165 maxPts += 4;
166 subpathCnt++;
167 }
168 GrPoint* base = new GrPoint[maxPts];
169 GrPoint* vert = base;
170 GrPoint* subpathBase = base;
171
172 GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
173
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000174 GrPoint pts[4];
reed@google.com07f3ee12011-05-16 17:21:57 +0000175 SkPath::Iter iter(path, true);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000176
177 bool first = true;
178 int subpath = 0;
179
180 for (;;) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000181 switch (iter.next(pts)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000182 case kMove_PathCmd:
183 if (!first) {
184 subpathVertCount[subpath] = vert-subpathBase;
185 subpathBase = vert;
186 ++subpath;
187 }
188 *vert = pts[0];
189 vert++;
190 break;
191 case kLine_PathCmd:
192 *vert = pts[1];
193 vert++;
194 break;
195 case kQuadratic_PathCmd: {
196 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
197 tolSqd, &vert,
198 GrPathUtils::quadraticPointCount(pts, tol));
199 break;
200 }
201 case kCubic_PathCmd: {
202 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
203 tolSqd, &vert,
204 GrPathUtils::cubicPointCount(pts, tol));
205 break;
206 }
207 case kClose_PathCmd:
208 break;
209 case kEnd_PathCmd:
210 subpathVertCount[subpath] = vert-subpathBase;
211 ++subpath; // this could be only in debug
212 goto FINISHED;
213 }
214 first = false;
215 }
216FINISHED:
217 if (translate) {
218 for (int i = 0; i < vert - base; i++) {
219 base[i].offset(translate->fX, translate->fY);
220 }
221 }
222
223 if (inverted) {
224 GrRect bounds;
225 GrAssert(NULL != target->getRenderTarget());
226 bounds.setLTRB(0, 0,
227 GrIntToScalar(target->getRenderTarget()->width()),
228 GrIntToScalar(target->getRenderTarget()->height()));
229 GrMatrix vmi;
230 if (target->getViewInverse(&vmi)) {
231 vmi.mapRect(&bounds);
232 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000233 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
234 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
235 *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
236 *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000237 subpathVertCount[subpath++] = 4;
238 }
239
240 GrAssert(subpath == subpathCnt);
241 GrAssert((vert - base) <= maxPts);
242
243 size_t count = vert - base;
244
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000245 if (count < 3) {
246 delete[] base;
247 return;
248 }
249
reed@google.com07f3ee12011-05-16 17:21:57 +0000250 if (subpathCnt == 1 && !inverted && path.isConvex()) {
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000251 if (target->isAntialiasState()) {
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000252 EdgeArray edges;
253 GrMatrix inverse, matrix = target->getViewMatrix();
254 target->getViewInverse(&inverse);
255
256 count = computeEdgesAndOffsetVertices(matrix, inverse, base, count, &edges);
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000257 size_t maxEdges = target->getMaxEdges();
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000258 if (count <= maxEdges) {
259 // All edges fit; upload all edges and draw all verts as a fan
260 target->setVertexSourceToArray(layout, base, count);
261 target->setEdgeAAData(&edges[0], count);
262 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
263 } else {
264 // Upload "maxEdges" edges and verts at a time, and draw as
265 // separate fans
266 for (size_t i = 0; i < count - 2; i += maxEdges - 2) {
267 edges[i] = edges[0];
268 base[i] = base[0];
269 int size = GR_CT_MIN(count - i, maxEdges);
270 target->setVertexSourceToArray(layout, &base[i], size);
271 target->setEdgeAAData(&edges[i], size);
272 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
273 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000274 }
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000275 target->setEdgeAAData(NULL, 0);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000276 } else {
277 target->setVertexSourceToArray(layout, base, count);
278 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
279 }
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000280 delete[] base;
281 return;
282 }
283
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000284 // FIXME: This copy could be removed if we had (templated?) versions of
285 // generate_*_point above that wrote directly into doubles.
286 double* inVertices = new double[count * 3];
287 for (size_t i = 0; i < count; ++i) {
288 inVertices[i * 3] = base[i].fX;
289 inVertices[i * 3 + 1] = base[i].fY;
290 inVertices[i * 3 + 2] = 1.0;
291 }
292
293 GLUtesselator* tess = internal_gluNewTess();
294 unsigned windingRule = fill_type_to_glu_winding_rule(fill);
295 internal_gluTessProperty(tess, GLU_TESS_WINDING_RULE, windingRule);
296 internal_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData);
297 internal_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData);
298 internal_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData);
299 internal_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData);
300 internal_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData);
301 GrTDArray<short> indices;
302 GrTDArray<GrPoint> vertices;
303 PolygonData data(&vertices, &indices);
304
305 internal_gluTessBeginPolygon(tess, &data);
306 size_t i = 0;
307 for (int sp = 0; sp < subpathCnt; ++sp) {
308 internal_gluTessBeginContour(tess);
309 int start = i;
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000310 size_t end = start + subpathVertCount[sp];
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000311 for (; i < end; ++i) {
312 double* inVertex = &inVertices[i * 3];
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000313 *vertices.append() = GrPoint::Make(inVertex[0], inVertex[1]);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000314 internal_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i));
315 }
316 internal_gluTessEndContour(tess);
317 }
318
319 internal_gluTessEndPolygon(tess);
320 internal_gluDeleteTess(tess);
321
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000322 if (indices.count() > 0) {
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000323 target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
324 target->setIndexSourceToArray(indices.begin(), indices.count());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000325 target->drawIndexed(kTriangles_PrimitiveType,
326 0,
327 0,
328 vertices.count(),
329 indices.count());
330 }
331 delete[] inVertices;
332 delete[] base;
333}
334
335bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000336 const SkPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000337 GrPathFill fill) const {
338 return kHairLine_PathFill != fill;
339}
340
341void GrTesselatedPathRenderer::drawPathToStencil(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000342 const SkPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000343 GrPathFill fill,
344 const GrPoint* translate) {
345 GrAlwaysAssert(!"multipass stencil should not be needed");
346}
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000347
348bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000349 const SkPath& path,
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000350 GrPathFill fill) {
351 int subpathCnt = 0;
352 int tol = GrPathUtils::gTolerance;
353 GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
354 return (subpathCnt == 1 &&
355 !IsFillInverted(fill) &&
reed@google.com07f3ee12011-05-16 17:21:57 +0000356 path.isConvex());
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000357}