blob: ff73a4143b9442dbf8e259350a288269e1b17eeb [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 {
bungeman@google.com8c5753e2011-05-20 19:11:50 +0000148 tol = GrScalarDiv(tol, stretch);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000149 }
150 GrScalar tolSqd = GrMul(tol, tol);
151
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000152 int subpathCnt;
153 int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
154
155 GrVertexLayout layout = 0;
156 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
157 if ((1 << s) & stages) {
158 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
159 }
160 }
161
162 bool inverted = IsFillInverted(fill);
163 if (inverted) {
164 maxPts += 4;
165 subpathCnt++;
166 }
167 GrPoint* base = new GrPoint[maxPts];
168 GrPoint* vert = base;
169 GrPoint* subpathBase = base;
170
171 GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
172
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000173 GrPoint pts[4];
reed@google.com07f3ee12011-05-16 17:21:57 +0000174 SkPath::Iter iter(path, true);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000175
176 bool first = true;
177 int subpath = 0;
178
179 for (;;) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000180 switch (iter.next(pts)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000181 case kMove_PathCmd:
182 if (!first) {
183 subpathVertCount[subpath] = vert-subpathBase;
184 subpathBase = vert;
185 ++subpath;
186 }
187 *vert = pts[0];
188 vert++;
189 break;
190 case kLine_PathCmd:
191 *vert = pts[1];
192 vert++;
193 break;
194 case kQuadratic_PathCmd: {
195 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
196 tolSqd, &vert,
197 GrPathUtils::quadraticPointCount(pts, tol));
198 break;
199 }
200 case kCubic_PathCmd: {
201 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
202 tolSqd, &vert,
203 GrPathUtils::cubicPointCount(pts, tol));
204 break;
205 }
206 case kClose_PathCmd:
207 break;
208 case kEnd_PathCmd:
209 subpathVertCount[subpath] = vert-subpathBase;
210 ++subpath; // this could be only in debug
211 goto FINISHED;
212 }
213 first = false;
214 }
215FINISHED:
216 if (translate) {
217 for (int i = 0; i < vert - base; i++) {
218 base[i].offset(translate->fX, translate->fY);
219 }
220 }
221
222 if (inverted) {
223 GrRect bounds;
224 GrAssert(NULL != target->getRenderTarget());
225 bounds.setLTRB(0, 0,
226 GrIntToScalar(target->getRenderTarget()->width()),
227 GrIntToScalar(target->getRenderTarget()->height()));
228 GrMatrix vmi;
229 if (target->getViewInverse(&vmi)) {
230 vmi.mapRect(&bounds);
231 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000232 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
233 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
234 *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
235 *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000236 subpathVertCount[subpath++] = 4;
237 }
238
239 GrAssert(subpath == subpathCnt);
240 GrAssert((vert - base) <= maxPts);
241
242 size_t count = vert - base;
243
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000244 if (count < 3) {
245 delete[] base;
246 return;
247 }
248
reed@google.com07f3ee12011-05-16 17:21:57 +0000249 if (subpathCnt == 1 && !inverted && path.isConvex()) {
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000250 if (target->isAntialiasState()) {
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000251 EdgeArray edges;
252 GrMatrix inverse, matrix = target->getViewMatrix();
253 target->getViewInverse(&inverse);
254
255 count = computeEdgesAndOffsetVertices(matrix, inverse, base, count, &edges);
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000256 size_t maxEdges = target->getMaxEdges();
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000257 if (count <= maxEdges) {
258 // All edges fit; upload all edges and draw all verts as a fan
259 target->setVertexSourceToArray(layout, base, count);
260 target->setEdgeAAData(&edges[0], count);
261 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
262 } else {
263 // Upload "maxEdges" edges and verts at a time, and draw as
264 // separate fans
265 for (size_t i = 0; i < count - 2; i += maxEdges - 2) {
266 edges[i] = edges[0];
267 base[i] = base[0];
268 int size = GR_CT_MIN(count - i, maxEdges);
269 target->setVertexSourceToArray(layout, &base[i], size);
270 target->setEdgeAAData(&edges[i], size);
271 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
272 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000273 }
senorblanco@chromium.orgef3913b2011-05-19 17:11:07 +0000274 target->setEdgeAAData(NULL, 0);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000275 } else {
276 target->setVertexSourceToArray(layout, base, count);
277 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
278 }
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000279 delete[] base;
280 return;
281 }
282
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000283 // FIXME: This copy could be removed if we had (templated?) versions of
284 // generate_*_point above that wrote directly into doubles.
285 double* inVertices = new double[count * 3];
286 for (size_t i = 0; i < count; ++i) {
287 inVertices[i * 3] = base[i].fX;
288 inVertices[i * 3 + 1] = base[i].fY;
289 inVertices[i * 3 + 2] = 1.0;
290 }
291
292 GLUtesselator* tess = internal_gluNewTess();
293 unsigned windingRule = fill_type_to_glu_winding_rule(fill);
294 internal_gluTessProperty(tess, GLU_TESS_WINDING_RULE, windingRule);
295 internal_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData);
296 internal_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData);
297 internal_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData);
298 internal_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData);
299 internal_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData);
300 GrTDArray<short> indices;
301 GrTDArray<GrPoint> vertices;
302 PolygonData data(&vertices, &indices);
303
304 internal_gluTessBeginPolygon(tess, &data);
305 size_t i = 0;
306 for (int sp = 0; sp < subpathCnt; ++sp) {
307 internal_gluTessBeginContour(tess);
308 int start = i;
senorblanco@chromium.org64cc5792011-05-19 19:58:58 +0000309 size_t end = start + subpathVertCount[sp];
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000310 for (; i < end; ++i) {
311 double* inVertex = &inVertices[i * 3];
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000312 *vertices.append() = GrPoint::Make(inVertex[0], inVertex[1]);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000313 internal_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i));
314 }
315 internal_gluTessEndContour(tess);
316 }
317
318 internal_gluTessEndPolygon(tess);
319 internal_gluDeleteTess(tess);
320
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000321 if (indices.count() > 0) {
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000322 target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
323 target->setIndexSourceToArray(indices.begin(), indices.count());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000324 target->drawIndexed(kTriangles_PrimitiveType,
325 0,
326 0,
327 vertices.count(),
328 indices.count());
329 }
330 delete[] inVertices;
331 delete[] base;
332}
333
334bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000335 const SkPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000336 GrPathFill fill) const {
337 return kHairLine_PathFill != fill;
338}
339
340void GrTesselatedPathRenderer::drawPathToStencil(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000341 const SkPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000342 GrPathFill fill,
343 const GrPoint* translate) {
344 GrAlwaysAssert(!"multipass stencil should not be needed");
345}
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000346
347bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000348 const SkPath& path,
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000349 GrPathFill fill) {
350 int subpathCnt = 0;
351 int tol = GrPathUtils::gTolerance;
352 GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
353 return (subpathCnt == 1 &&
354 !IsFillInverted(fill) &&
reed@google.com07f3ee12011-05-16 17:21:57 +0000355 path.isConvex());
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000356}