blob: da6da5cad89341d40afe540ba275edfec6421962 [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.org92e0f222011-05-12 15:49:15 +000088class Edge {
89 public:
90 Edge() {}
91 Edge(float x, float y, float z) : fX(x), fY(y), fZ(z) {}
92 GrPoint intersect(const Edge& other) {
93 return GrPoint::Make(
94 (fY * other.fZ - other.fY * fZ) / (fX * other.fY - other.fX * fY),
95 (fX * other.fZ - other.fX * fZ) / (other.fX * fY - fX * other.fY));
96 }
97 float fX, fY, fZ;
98};
99
100typedef GrTDArray<Edge> EdgeArray;
101
senorblanco@chromium.orgff174b32011-05-16 16:59:57 +0000102bool isCCW(const GrPoint* pts)
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000103{
senorblanco@chromium.orgff174b32011-05-16 16:59:57 +0000104 GrVec v1 = pts[1] - pts[0];
105 GrVec v2 = pts[2] - pts[1];
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000106 return v1.cross(v2) < 0;
107}
108
109static size_t computeEdgesAndOffsetVertices(const GrMatrix& matrix,
110 const GrMatrix& inverse,
111 GrPoint* vertices,
112 size_t numVertices,
113 EdgeArray* edges)
114{
senorblanco@chromium.orgff174b32011-05-16 16:59:57 +0000115 matrix.mapPoints(vertices, numVertices);
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000116 GrPoint p = vertices[numVertices - 1];
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000117 float sign = isCCW(vertices) ? -1.0f : 1.0f;
118 for (size_t i = 0; i < numVertices; ++i) {
119 GrPoint q = vertices[i];
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000120 if (p == q) continue;
121 GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
122 float scale = sign / tangent.length();
123 float cross2 = p.fX * q.fY - q.fX * p.fY;
124 Edge edge(tangent.fX * scale,
125 tangent.fY * scale,
126 cross2 * scale + 0.5f);
127 *edges->append() = edge;
128 p = q;
129 }
130 Edge prev_edge = *edges->back();
131 for (size_t i = 0; i < edges->count(); ++i) {
132 Edge edge = edges->at(i);
133 vertices[i] = prev_edge.intersect(edge);
134 inverse.mapPoints(&vertices[i], 1);
135 prev_edge = edge;
136 }
137 return edges->count();
138}
139
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000140void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
141 GrDrawTarget::StageBitfield stages,
reed@google.com07f3ee12011-05-16 17:21:57 +0000142 const GrPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000143 GrPathFill fill,
144 const GrPoint* translate) {
145 GrDrawTarget::AutoStateRestore asr(target);
146 bool colorWritesWereDisabled = target->isColorWriteDisabled();
147 // face culling doesn't make sense here
148 GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
149
150 GrMatrix viewM = target->getViewMatrix();
151 // In order to tesselate the path we get a bound on how much the matrix can
152 // stretch when mapping to screen coordinates.
153 GrScalar stretch = viewM.getMaxStretch();
154 bool useStretch = stretch > 0;
155 GrScalar tol = GrPathUtils::gTolerance;
156
157 if (!useStretch) {
158 // TODO: deal with perspective in some better way.
159 tol /= 10;
160 } else {
161 GrScalar sinv = GR_Scalar1 / stretch;
162 tol = GrMul(tol, sinv);
163 }
164 GrScalar tolSqd = GrMul(tol, tol);
165
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000166 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
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000187 GrPoint pts[4];
reed@google.com07f3ee12011-05-16 17:21:57 +0000188 SkPath::Iter iter(path, true);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000189
190 bool first = true;
191 int subpath = 0;
192
193 for (;;) {
reed@google.com07f3ee12011-05-16 17:21:57 +0000194 switch (iter.next(pts)) {
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000195 case kMove_PathCmd:
196 if (!first) {
197 subpathVertCount[subpath] = vert-subpathBase;
198 subpathBase = vert;
199 ++subpath;
200 }
201 *vert = pts[0];
202 vert++;
203 break;
204 case kLine_PathCmd:
205 *vert = pts[1];
206 vert++;
207 break;
208 case kQuadratic_PathCmd: {
209 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
210 tolSqd, &vert,
211 GrPathUtils::quadraticPointCount(pts, tol));
212 break;
213 }
214 case kCubic_PathCmd: {
215 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
216 tolSqd, &vert,
217 GrPathUtils::cubicPointCount(pts, tol));
218 break;
219 }
220 case kClose_PathCmd:
221 break;
222 case kEnd_PathCmd:
223 subpathVertCount[subpath] = vert-subpathBase;
224 ++subpath; // this could be only in debug
225 goto FINISHED;
226 }
227 first = false;
228 }
229FINISHED:
230 if (translate) {
231 for (int i = 0; i < vert - base; i++) {
232 base[i].offset(translate->fX, translate->fY);
233 }
234 }
235
236 if (inverted) {
237 GrRect bounds;
238 GrAssert(NULL != target->getRenderTarget());
239 bounds.setLTRB(0, 0,
240 GrIntToScalar(target->getRenderTarget()->width()),
241 GrIntToScalar(target->getRenderTarget()->height()));
242 GrMatrix vmi;
243 if (target->getViewInverse(&vmi)) {
244 vmi.mapRect(&bounds);
245 }
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000246 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
247 *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
248 *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
249 *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000250 subpathVertCount[subpath++] = 4;
251 }
252
253 GrAssert(subpath == subpathCnt);
254 GrAssert((vert - base) <= maxPts);
255
256 size_t count = vert - base;
257
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000258 if (count < 3) {
259 delete[] base;
260 return;
261 }
262
reed@google.com07f3ee12011-05-16 17:21:57 +0000263 if (subpathCnt == 1 && !inverted && path.isConvex()) {
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000264 if (target->isAntialiasState()) {
265 target->enableState(GrDrawTarget::kEdgeAA_StateBit);
266 EdgeArray edges;
267 GrMatrix inverse, matrix = target->getViewMatrix();
268 target->getViewInverse(&inverse);
269
270 count = computeEdgesAndOffsetVertices(matrix, inverse, base, count, &edges);
271 GrPoint triangle[3];
272 triangle[0] = base[0];
273 Edge triangleEdges[6];
274 triangleEdges[0] = *edges.back();
275 triangleEdges[1] = edges[0];
276 for (size_t i = 1; i < count - 1; i++) {
277 triangle[1] = base[i];
278 triangle[2] = base[i + 1];
279 triangleEdges[2] = edges[i - 1];
280 triangleEdges[3] = edges[i];
281 triangleEdges[4] = edges[i];
282 triangleEdges[5] = edges[i + 1];
283 target->setVertexSourceToArray(layout, triangle, 3);
284 target->setEdgeAAData(&triangleEdges[0].fX);
285 target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
286 }
287 target->disableState(GrDrawTarget::kEdgeAA_StateBit);
288 } else {
289 target->setVertexSourceToArray(layout, base, count);
290 target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
291 }
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000292 delete[] base;
293 return;
294 }
295
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000296 // FIXME: This copy could be removed if we had (templated?) versions of
297 // generate_*_point above that wrote directly into doubles.
298 double* inVertices = new double[count * 3];
299 for (size_t i = 0; i < count; ++i) {
300 inVertices[i * 3] = base[i].fX;
301 inVertices[i * 3 + 1] = base[i].fY;
302 inVertices[i * 3 + 2] = 1.0;
303 }
304
305 GLUtesselator* tess = internal_gluNewTess();
306 unsigned windingRule = fill_type_to_glu_winding_rule(fill);
307 internal_gluTessProperty(tess, GLU_TESS_WINDING_RULE, windingRule);
308 internal_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData);
309 internal_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData);
310 internal_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData);
311 internal_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData);
312 internal_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData);
313 GrTDArray<short> indices;
314 GrTDArray<GrPoint> vertices;
315 PolygonData data(&vertices, &indices);
316
317 internal_gluTessBeginPolygon(tess, &data);
318 size_t i = 0;
319 for (int sp = 0; sp < subpathCnt; ++sp) {
320 internal_gluTessBeginContour(tess);
321 int start = i;
322 int end = start + subpathVertCount[sp];
323 for (; i < end; ++i) {
324 double* inVertex = &inVertices[i * 3];
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000325 *vertices.append() = GrPoint::Make(inVertex[0], inVertex[1]);
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000326 internal_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i));
327 }
328 internal_gluTessEndContour(tess);
329 }
330
331 internal_gluTessEndPolygon(tess);
332 internal_gluDeleteTess(tess);
333
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000334 if (indices.count() > 0) {
senorblanco@chromium.orgcf3edc92011-03-29 17:42:30 +0000335 target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
336 target->setIndexSourceToArray(indices.begin(), indices.count());
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000337 target->drawIndexed(kTriangles_PrimitiveType,
338 0,
339 0,
340 vertices.count(),
341 indices.count());
342 }
343 delete[] inVertices;
344 delete[] base;
345}
346
347bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000348 const SkPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000349 GrPathFill fill) const {
350 return kHairLine_PathFill != fill;
351}
352
353void GrTesselatedPathRenderer::drawPathToStencil(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000354 const SkPath& path,
senorblanco@chromium.org9d18b782011-03-28 20:47:09 +0000355 GrPathFill fill,
356 const GrPoint* translate) {
357 GrAlwaysAssert(!"multipass stencil should not be needed");
358}
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000359
360bool GrTesselatedPathRenderer::supportsAA(GrDrawTarget* target,
reed@google.com07f3ee12011-05-16 17:21:57 +0000361 const SkPath& path,
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000362 GrPathFill fill) {
363 int subpathCnt = 0;
364 int tol = GrPathUtils::gTolerance;
365 GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
366 return (subpathCnt == 1 &&
367 !IsFillInverted(fill) &&
reed@google.com07f3ee12011-05-16 17:21:57 +0000368 path.isConvex());
senorblanco@chromium.org92e0f222011-05-12 15:49:15 +0000369}