blob: c47b6e522c2e686c0ecc25f887ccdafe3761ef81 [file] [log] [blame]
bsalomon@google.comffca4002011-02-22 20:34:01 +00001#include "GrPathRenderer.h"
2
3#include "GrPoint.h"
4#include "GrDrawTarget.h"
5#include "GrPathIter.h"
6#include "GrMemory.h"
7#include "GrTexture.h"
8
9
10
11GrDefaultPathRenderer::GrDefaultPathRenderer(bool singlePassWindingStencil)
12 : fSinglePassWindingStencil(singlePassWindingStencil) {
13
14}
15
16////////////////////////////////////////////////////////////////////////////////
17// Helpers for draw Path
18
19#define STENCIL_OFF 0 // Always disable stencil (even when needed)
20static const GrScalar gTolerance = GR_Scalar1;
21
22static const uint32_t MAX_POINTS_PER_CURVE = 1 << 10;
23
24static uint32_t quadratic_point_count(const GrPoint points[], GrScalar tol) {
25 GrScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);
26 if (d < tol) {
27 return 1;
28 } else {
29 // Each time we subdivide, d should be cut in 4. So we need to
30 // subdivide x = log4(d/tol) times. x subdivisions creates 2^(x)
31 // points.
32 // 2^(log4(x)) = sqrt(x);
33 d = ceilf(sqrtf(d/tol));
34 return GrMin(GrNextPow2((uint32_t)d), MAX_POINTS_PER_CURVE);
35 }
36}
37
38static uint32_t generate_quadratic_points(const GrPoint& p0,
39 const GrPoint& p1,
40 const GrPoint& p2,
41 GrScalar tolSqd,
42 GrPoint** points,
43 uint32_t pointsLeft) {
44 if (pointsLeft < 2 ||
45 (p1.distanceToLineSegmentBetweenSqd(p0, p2)) < tolSqd) {
46 (*points)[0] = p2;
47 *points += 1;
48 return 1;
49 }
50
51 GrPoint q[] = {
52 GrPoint(GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY)),
53 GrPoint(GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY)),
54 };
55 GrPoint r(GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY));
56
57 pointsLeft >>= 1;
58 uint32_t a = generate_quadratic_points(p0, q[0], r, tolSqd, points, pointsLeft);
59 uint32_t b = generate_quadratic_points(r, q[1], p2, tolSqd, points, pointsLeft);
60 return a + b;
61}
62
63static uint32_t cubic_point_count(const GrPoint points[], GrScalar tol) {
64 GrScalar d = GrMax(points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),
65 points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
66 d = sqrtf(d);
67 if (d < tol) {
68 return 1;
69 } else {
70 d = ceilf(sqrtf(d/tol));
71 return GrMin(GrNextPow2((uint32_t)d), MAX_POINTS_PER_CURVE);
72 }
73}
74
75static uint32_t generate_cubic_points(const GrPoint& p0,
76 const GrPoint& p1,
77 const GrPoint& p2,
78 const GrPoint& p3,
79 GrScalar tolSqd,
80 GrPoint** points,
81 uint32_t pointsLeft) {
82 if (pointsLeft < 2 ||
83 (p1.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd &&
84 p2.distanceToLineSegmentBetweenSqd(p0, p3) < tolSqd)) {
85 (*points)[0] = p3;
86 *points += 1;
87 return 1;
88 }
89 GrPoint q[] = {
90 GrPoint(GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY)),
91 GrPoint(GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY)),
92 GrPoint(GrScalarAve(p2.fX, p3.fX), GrScalarAve(p2.fY, p3.fY))
93 };
94 GrPoint r[] = {
95 GrPoint(GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY)),
96 GrPoint(GrScalarAve(q[1].fX, q[2].fX), GrScalarAve(q[1].fY, q[2].fY))
97 };
98 GrPoint s(GrScalarAve(r[0].fX, r[1].fX), GrScalarAve(r[0].fY, r[1].fY));
99 pointsLeft >>= 1;
100 uint32_t a = generate_cubic_points(p0, q[0], r[0], s, tolSqd, points, pointsLeft);
101 uint32_t b = generate_cubic_points(s, r[1], q[2], p3, tolSqd, points, pointsLeft);
102 return a + b;
103}
104
105static int worst_case_point_count(GrPathIter* path,
106 int* subpaths,
107 GrScalar tol) {
108 int pointCount = 0;
109 *subpaths = 1;
110
111 bool first = true;
112
113 GrPathIter::Command cmd;
114
115 GrPoint pts[4];
116 while ((cmd = path->next(pts)) != GrPathIter::kEnd_Command) {
117
118 switch (cmd) {
119 case GrPathIter::kLine_Command:
120 pointCount += 1;
121 break;
122 case GrPathIter::kQuadratic_Command:
123 pointCount += quadratic_point_count(pts, tol);
124 break;
125 case GrPathIter::kCubic_Command:
126 pointCount += cubic_point_count(pts, tol);
127 break;
128 case GrPathIter::kMove_Command:
129 pointCount += 1;
130 if (!first) {
131 ++(*subpaths);
132 }
133 break;
134 default:
135 break;
136 }
137 first = false;
138 }
139 return pointCount;
140}
141
142static inline bool single_pass_path(const GrPathIter& path,
143 GrPathFill fill,
144 const GrDrawTarget& target) {
145#if STENCIL_OFF
146 return true;
147#else
148 if (kEvenOdd_PathFill == fill) {
149 GrPathIter::ConvexHint hint = path.hint();
150 return hint == GrPathIter::kConvex_ConvexHint ||
151 hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint;
152 } else if (kWinding_PathFill == fill) {
153 GrPathIter::ConvexHint hint = path.hint();
154 return hint == GrPathIter::kConvex_ConvexHint ||
155 hint == GrPathIter::kNonOverlappingConvexPieces_ConvexHint ||
156 (hint == GrPathIter::kSameWindingConvexPieces_ConvexHint &&
157 target.canDisableBlend() && !target.isDitherState());
158
159 }
160 return false;
161#endif
162}
163
164void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
165 GrDrawTarget::StageBitfield stages,
166 GrPathIter* path,
167 GrPathFill fill,
168 const GrPoint* translate) {
169
170 GrDrawTarget::AutoStateRestore asr(target);
171
172 GrMatrix viewM = target->getViewMatrix();
173 // In order to tesselate the path we get a bound on how much the matrix can
174 // stretch when mapping to screen coordinates.
175 GrScalar stretch = viewM.getMaxStretch();
176 bool useStretch = stretch > 0;
177 GrScalar tol = gTolerance;
178
179 if (!useStretch) {
180 // TODO: deal with perspective in some better way.
181 tol /= 10;
182 } else {
183 GrScalar sinv = GR_Scalar1 / stretch;
184 tol = GrMul(tol, sinv);
185 }
186 GrScalar tolSqd = GrMul(tol, tol);
187
188 int subpathCnt;
189 int maxPts = worst_case_point_count(path,
190 &subpathCnt,
191 tol);
192
193 GrVertexLayout layout = 0;
194 for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
195 if ((1 << s) & stages) {
196 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
197 }
198 }
199
200 // add 4 to hold the bounding rect
201 GrDrawTarget::AutoReleaseGeometry arg(target, layout, maxPts + 4, 0);
202
203 GrPoint* base = (GrPoint*) arg.vertices();
204 GrPoint* vert = base;
205 GrPoint* subpathBase = base;
206
207 GrAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
208
209 path->rewind();
210
211 // TODO: use primitve restart if available rather than multiple draws
212 GrPrimitiveType type;
213 int passCount = 0;
214 GrDrawTarget::StencilPass passes[3];
215 bool reverse = false;
216
217 if (kHairLine_PathFill == fill) {
218 type = kLineStrip_PrimitiveType;
219 passCount = 1;
220 passes[0] = GrDrawTarget::kNone_StencilPass;
221 } else {
222 type = kTriangleFan_PrimitiveType;
223 if (single_pass_path(*path, fill, *target)) {
224 passCount = 1;
225 passes[0] = GrDrawTarget::kNone_StencilPass;
226 } else {
227 switch (fill) {
228 case kInverseEvenOdd_PathFill:
229 reverse = true;
230 // fallthrough
231 case kEvenOdd_PathFill:
232 passCount = 2;
233 passes[0] = GrDrawTarget::kEvenOddStencil_StencilPass;
234 passes[1] = GrDrawTarget::kEvenOddColor_StencilPass;
235 break;
236
237 case kInverseWinding_PathFill:
238 reverse = true;
239 // fallthrough
240 case kWinding_PathFill:
241 passes[0] = GrDrawTarget::kWindingStencil1_StencilPass;
242 if (fSinglePassWindingStencil) {
243 passes[1] = GrDrawTarget::kWindingColor_StencilPass;
244 passCount = 2;
245 } else {
246 passes[1] = GrDrawTarget::kWindingStencil2_StencilPass;
247 passes[2] = GrDrawTarget::kWindingColor_StencilPass;
248 passCount = 3;
249 }
250 break;
251 default:
252 GrAssert(!"Unknown path fill!");
253 return;
254 }
255 }
256 }
257 target->setReverseFill(reverse);
258
259 GrPoint pts[4];
260
261 bool first = true;
262 int subpath = 0;
263
264 for (;;) {
265 GrPathIter::Command cmd = path->next(pts);
266 switch (cmd) {
267 case GrPathIter::kMove_Command:
268 if (!first) {
269 subpathVertCount[subpath] = vert-subpathBase;
270 subpathBase = vert;
271 ++subpath;
272 }
273 *vert = pts[0];
274 vert++;
275 break;
276 case GrPathIter::kLine_Command:
277 *vert = pts[1];
278 vert++;
279 break;
280 case GrPathIter::kQuadratic_Command: {
281 generate_quadratic_points(pts[0], pts[1], pts[2],
282 tolSqd, &vert,
283 quadratic_point_count(pts, tol));
284 break;
285 }
286 case GrPathIter::kCubic_Command: {
287 generate_cubic_points(pts[0], pts[1], pts[2], pts[3],
288 tolSqd, &vert,
289 cubic_point_count(pts, tol));
290 break;
291 }
292 case GrPathIter::kClose_Command:
293 break;
294 case GrPathIter::kEnd_Command:
295 subpathVertCount[subpath] = vert-subpathBase;
296 ++subpath; // this could be only in debug
297 goto FINISHED;
298 }
299 first = false;
300 }
301FINISHED:
302 GrAssert(subpath == subpathCnt);
303 GrAssert((vert - base) <= maxPts);
304
305 if (translate) {
306 int count = vert - base;
307 for (int i = 0; i < count; i++) {
308 base[i].offset(translate->fX, translate->fY);
309 }
310 }
311
312 // arbitrary path complexity cutoff
313 bool useBounds = fill != kHairLine_PathFill &&
314 (reverse || (vert - base) > 8);
315 GrPoint* boundsVerts = base + maxPts;
316 if (useBounds) {
317 GrRect bounds;
318 if (reverse) {
319 GrAssert(NULL != target->getRenderTarget());
320 // draw over the whole world.
321 bounds.setLTRB(0, 0,
322 GrIntToScalar(target->getRenderTarget()->width()),
323 GrIntToScalar(target->getRenderTarget()->height()));
324 GrMatrix vmi;
325 if (target->getViewInverse(&vmi)) {
326 vmi.mapRect(&bounds);
327 }
328 } else {
329 bounds.setBounds((GrPoint*)base, vert - base);
330 }
331 boundsVerts[0].setRectFan(bounds.fLeft, bounds.fTop, bounds.fRight,
332 bounds.fBottom);
333 }
334
335 for (int p = 0; p < passCount; ++p) {
336 target->setStencilPass(passes[p]);
337 if (useBounds && (GrDrawTarget::kEvenOddColor_StencilPass == passes[p] ||
338 GrDrawTarget::kWindingColor_StencilPass == passes[p])) {
339 target->drawNonIndexed(kTriangleFan_PrimitiveType,
340 maxPts, 4);
341
342 } else {
343 int baseVertex = 0;
344 for (int sp = 0; sp < subpathCnt; ++sp) {
345 target->drawNonIndexed(type,
346 baseVertex,
347 subpathVertCount[sp]);
348 baseVertex += subpathVertCount[sp];
349 }
350 }
351 }
352}