blob: 95e8abe4edb3fca18ef5e5f1c1d0cef0ec4afc10 [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();
58 *polygonData->fVertices->append() = GrPoint(static_cast<float>(coords[0]),
59 static_cast<float>(coords[1]));
60 *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
86void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
87 GrDrawTarget::StageBitfield stages,
88 GrPathIter* path,
89 GrPathFill fill,
90 const GrPoint* translate) {
91 GrDrawTarget::AutoStateRestore asr(target);
92 bool colorWritesWereDisabled = target->isColorWriteDisabled();
93 // face culling doesn't make sense here
94 GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
95
96 GrMatrix viewM = target->getViewMatrix();
97 // In order to tesselate the path we get a bound on how much the matrix can
98 // stretch when mapping to screen coordinates.
99 GrScalar stretch = viewM.getMaxStretch();
100 bool useStretch = stretch > 0;
101 GrScalar tol = GrPathUtils::gTolerance;
102
103 if (!useStretch) {
104 // TODO: deal with perspective in some better way.
105 tol /= 10;
106 } else {
107 GrScalar sinv = GR_Scalar1 / stretch;
108 tol = GrMul(tol, sinv);
109 }
110 GrScalar tolSqd = GrMul(tol, tol);
111
112 path->rewind();
113
114 int subpathCnt;
115 int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
116
117 GrVertexLayout layout = 0;
118 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
119 if ((1 << s) & stages) {
120 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
121 }
122 }
123
124 bool inverted = IsFillInverted(fill);
125 if (inverted) {
126 maxPts += 4;
127 subpathCnt++;
128 }
129 GrPoint* base = new GrPoint[maxPts];
130 GrPoint* vert = base;
131 GrPoint* subpathBase = base;
132
133 GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
134
135 path->rewind();
136
137 GrPoint pts[4];
138
139 bool first = true;
140 int subpath = 0;
141
142 for (;;) {
143 GrPathCmd cmd = path->next(pts);
144 switch (cmd) {
145 case kMove_PathCmd:
146 if (!first) {
147 subpathVertCount[subpath] = vert-subpathBase;
148 subpathBase = vert;
149 ++subpath;
150 }
151 *vert = pts[0];
152 vert++;
153 break;
154 case kLine_PathCmd:
155 *vert = pts[1];
156 vert++;
157 break;
158 case kQuadratic_PathCmd: {
159 GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
160 tolSqd, &vert,
161 GrPathUtils::quadraticPointCount(pts, tol));
162 break;
163 }
164 case kCubic_PathCmd: {
165 GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
166 tolSqd, &vert,
167 GrPathUtils::cubicPointCount(pts, tol));
168 break;
169 }
170 case kClose_PathCmd:
171 break;
172 case kEnd_PathCmd:
173 subpathVertCount[subpath] = vert-subpathBase;
174 ++subpath; // this could be only in debug
175 goto FINISHED;
176 }
177 first = false;
178 }
179FINISHED:
180 if (translate) {
181 for (int i = 0; i < vert - base; i++) {
182 base[i].offset(translate->fX, translate->fY);
183 }
184 }
185
186 if (inverted) {
187 GrRect bounds;
188 GrAssert(NULL != target->getRenderTarget());
189 bounds.setLTRB(0, 0,
190 GrIntToScalar(target->getRenderTarget()->width()),
191 GrIntToScalar(target->getRenderTarget()->height()));
192 GrMatrix vmi;
193 if (target->getViewInverse(&vmi)) {
194 vmi.mapRect(&bounds);
195 }
196 *vert++ = GrPoint(bounds.fLeft, bounds.fTop);
197 *vert++ = GrPoint(bounds.fLeft, bounds.fBottom);
198 *vert++ = GrPoint(bounds.fRight, bounds.fBottom);
199 *vert++ = GrPoint(bounds.fRight, bounds.fTop);
200 subpathVertCount[subpath++] = 4;
201 }
202
203 GrAssert(subpath == subpathCnt);
204 GrAssert((vert - base) <= maxPts);
205
206 size_t count = vert - base;
207
208 // FIXME: This copy could be removed if we had (templated?) versions of
209 // generate_*_point above that wrote directly into doubles.
210 double* inVertices = new double[count * 3];
211 for (size_t i = 0; i < count; ++i) {
212 inVertices[i * 3] = base[i].fX;
213 inVertices[i * 3 + 1] = base[i].fY;
214 inVertices[i * 3 + 2] = 1.0;
215 }
216
217 GLUtesselator* tess = internal_gluNewTess();
218 unsigned windingRule = fill_type_to_glu_winding_rule(fill);
219 internal_gluTessProperty(tess, GLU_TESS_WINDING_RULE, windingRule);
220 internal_gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginData);
221 internal_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexData);
222 internal_gluTessCallback(tess, GLU_TESS_END_DATA, (TESSCB) &endData);
223 internal_gluTessCallback(tess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagData);
224 internal_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineData);
225 GrTDArray<short> indices;
226 GrTDArray<GrPoint> vertices;
227 PolygonData data(&vertices, &indices);
228
229 internal_gluTessBeginPolygon(tess, &data);
230 size_t i = 0;
231 for (int sp = 0; sp < subpathCnt; ++sp) {
232 internal_gluTessBeginContour(tess);
233 int start = i;
234 int end = start + subpathVertCount[sp];
235 for (; i < end; ++i) {
236 double* inVertex = &inVertices[i * 3];
237 *vertices.append() = GrPoint(inVertex[0], inVertex[1]);
238 internal_gluTessVertex(tess, inVertex, reinterpret_cast<void*>(i));
239 }
240 internal_gluTessEndContour(tess);
241 }
242
243 internal_gluTessEndPolygon(tess);
244 internal_gluDeleteTess(tess);
245
246 // FIXME: If we could figure out the maxIndices before running the
247 // tesselator, we could allocate the geometry upfront, rather than making
248 // yet another copy.
249 GrDrawTarget::AutoReleaseGeometry geom(target, layout, vertices.count(), indices.count());
250
251 memcpy(geom.vertices(), vertices.begin(), vertices.count() * sizeof(GrPoint));
252 memcpy(geom.indices(), indices.begin(), indices.count() * sizeof(short));
253
254 if (indices.count() > 0) {
255 target->drawIndexed(kTriangles_PrimitiveType,
256 0,
257 0,
258 vertices.count(),
259 indices.count());
260 }
261 delete[] inVertices;
262 delete[] base;
263}
264
265bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
266 GrPathIter* path,
267 GrPathFill fill) const {
268 return kHairLine_PathFill != fill;
269}
270
271void GrTesselatedPathRenderer::drawPathToStencil(GrDrawTarget* target,
272 GrPathIter* path,
273 GrPathFill fill,
274 const GrPoint* translate) {
275 GrAlwaysAssert(!"multipass stencil should not be needed");
276}