blob: 7260962b18a0f140ea1efd66048b7d3cceb22907 [file] [log] [blame]
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +00001
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "GrAAConvexPathRenderer.h"
10
11#include "GrContext.h"
12#include "GrDrawState.h"
13#include "GrPathUtils.h"
14#include "SkString.h"
15#include "SkTrace.h"
16
17
18GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
19}
20
21bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget::Caps& targetCaps,
22 const SkPath& path,
23 GrPathFill fill,
24 bool antiAlias) const {
25 return targetCaps.fShaderDerivativeSupport && antiAlias &&
26 kHairLine_PathFill != fill && !GrIsFillInverted(fill) &&
27 path.isConvex();
28}
29
30namespace {
31
32
33struct Segment {
34 enum {
35 kLine,
36 kQuad
37 } fType;
38 // line uses a, quad uses a and b (first point comes from prev. segment)
39 GrPoint fA, fB;
40 // normal to edge ending at a and b
41 GrVec fANorm, fBNorm;
42 // mid vector at a that splits angle with previous edge
43 GrVec fPrevMid;
44};
45
46typedef SkTArray<Segment, true> SegmentArray;
47
48bool is_path_degenerate(const GrPath& path) {
49 int n = path.countPoints();
50 if (n < 3) {
51 return true;
52 }
53
54 // compute a line from the first two points that are not equal, look for
55 // a third pt that is off the line.
56 static const SkScalar TOL = (SK_Scalar1 / 16);
57 bool foundLine = false;
58 GrPoint firstPoint = path.getPoint(0);
59 GrVec lineV;
60 SkScalar lineC;
61 int i = 1;
62
63 do {
64 GrPoint pt = path.getPoint(i);
65 if (!foundLine) {
66 if (pt != firstPoint) {
67 lineV = pt - firstPoint;
68 lineV.normalize();
69 lineV.setOrthog(lineV);
70 lineC = lineV.dot(firstPoint);
71 foundLine = true;
72 }
73 } else {
74 if (SkScalarAbs(lineV.dot(pt) - lineC) > TOL) {
75 return false;
76 }
77 }
78 ++i;
79 } while (i < n);
80 return true;
81}
82
83bool get_segments(const GrPath& path,
84 SegmentArray* segments,
85 int* quadCnt,
86 int* lineCnt) {
87 *quadCnt = 0;
88 *lineCnt = 0;
89 SkPath::Iter iter(path, true);
90 // This renderer overemphasis very thin paths (every pixel intersected by
91 // the path will be at least 1/2 on). When the path degenerates to a line
92 // this makes the path draw as a hairline. This is a pretty glaring error
93 // so we detect this case and will not draw.
94 if (is_path_degenerate(path)) {
95 return false;
96 }
97 for (;;) {
98 GrPoint pts[4];
99 GrPathCmd cmd = (GrPathCmd)iter.next(pts);
100 switch (cmd) {
101 case kLine_PathCmd: {
102 segments->push_back();
103 segments->back().fType = Segment::kLine;
104 segments->back().fA = pts[1];
105 ++(*lineCnt);
106 break;
107 }
108 case kQuadratic_PathCmd:
109 segments->push_back();
110 segments->back().fType = Segment::kQuad;
111 segments->back().fA = pts[1];
112 segments->back().fB = pts[2];
113 ++(*quadCnt);
114 break;
115 case kCubic_PathCmd: {
116 SkSTArray<15, SkPoint, true> quads;
117 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, &quads);
118 int count = quads.count();
119 for (int q = 0; q < count; q += 3) {
120 segments->push_back();
121 segments->back().fType = Segment::kQuad;
122 segments->back().fA = quads[q + 1];
123 segments->back().fB = quads[q + 2];
124 ++(*quadCnt);
125 }
126 break;
127 };
128 case kEnd_PathCmd:
129 GrAssert(*quadCnt + *lineCnt == segments->count());
130 return true;
131 default:
132 break;
133 }
134 }
135}
136
137struct QuadVertex {
138 GrPoint fPos;
139 union {
140 GrPoint fQuadUV;
141 GrScalar fEdge[4];
142 };
143};
144
145void get_counts(int quadCount, int lineCount, int* vCount, int* iCount) {
146 *vCount = 9 * lineCount + 11 * quadCount;
147 *iCount = 15 * lineCount + 24 * quadCount;
148}
149
150// for visual debugging, exagerate the AA smear at the edges
151// requires modifying the distance calc in the shader actually shade differently
152//#define STRETCH_AA
153#define STRETCH_FACTOR (20 * SK_Scalar1)
154
155void create_vertices(SegmentArray* segments,
156 const GrPoint& fanPt,
157 QuadVertex* verts,
158 uint16_t* idxs) {
159 int count = segments->count();
160 GrAssert(count > 1);
161 int prevS = count - 1;
162 const Segment& lastSeg = (*segments)[prevS];
163
164 // walk the segments and compute normals to each edge and
165 // bisectors at vertices. The loop relies on having the end point and normal
166 // from previous segment so we first compute that. Also, we determine
167 // whether normals point left or right to face outside the path.
168 GrVec prevPt;
169 GrPoint prevPrevPt;
170 GrVec prevNorm;
171 if (Segment::kLine == lastSeg.fType) {
172 prevPt = lastSeg.fA;
173 const Segment& secondLastSeg = (*segments)[prevS - 1];
174 prevPrevPt = (Segment::kLine == secondLastSeg.fType) ?
175 secondLastSeg.fA :
176 secondLastSeg.fB;
177 } else {
178 prevPt = lastSeg.fB;
179 prevPrevPt = lastSeg.fA;
180 }
181 GrVec::Side outside;
182 // we will compute our edge vectors so that they are pointing along the
183 // direction in which we are iterating the path. So here we take an opposite
184 // vector and get the side that the fan pt lies relative to it.
185 fanPt.distanceToLineBetweenSqd(prevPrevPt, prevPt, &outside);
186 prevNorm = prevPt - prevPrevPt;
187 prevNorm.normalize();
188 prevNorm.setOrthog(prevNorm, outside);
189#ifdef STRETCH_AA
190 prevNorm.scale(STRETCH_FACTOR);
191#endif
192
193 // compute the normals and bisectors
194 for (int s = 0; s < count; ++s, ++prevS) {
195 Segment& curr = (*segments)[s];
196
197 GrVec currVec = curr.fA - prevPt;
198 currVec.normalize();
199 curr.fANorm.setOrthog(currVec, outside);
200#ifdef STRETCH_AA
201 curr.fANorm.scale(STRETCH_FACTOR);
202#endif
203 curr.fPrevMid = prevNorm + curr.fANorm;
204 curr.fPrevMid.normalize();
205#ifdef STRETCH_AA
206 curr.fPrevMid.scale(STRETCH_FACTOR);
207#endif
208 if (Segment::kLine == curr.fType) {
209 prevPt = curr.fA;
210 prevNorm = curr.fANorm;
211 } else {
212 currVec = curr.fB - curr.fA;
213 currVec.normalize();
214 curr.fBNorm.setOrthog(currVec, outside);
215#ifdef STRETCH_AA
216 curr.fBNorm.scale(STRETCH_FACTOR);
217#endif
218 prevPt = curr.fB;
219 prevNorm = curr.fBNorm;
220 }
221 }
222
223 // compute the vertices / indices
224 if (Segment::kLine == lastSeg.fType) {
225 prevPt = lastSeg.fA;
226 prevNorm = lastSeg.fANorm;
227 } else {
228 prevPt = lastSeg.fB;
229 prevNorm = lastSeg.fBNorm;
230 }
231 int v = 0;
232 int i = 0;
233 for (int s = 0; s < count; ++s, ++prevS) {
234 Segment& curr = (*segments)[s];
235 verts[v + 0].fPos = prevPt;
236 verts[v + 1].fPos = prevPt + prevNorm;
237 verts[v + 2].fPos = prevPt + curr.fPrevMid;
238 verts[v + 3].fPos = prevPt + curr.fANorm;
239 verts[v + 0].fQuadUV.set(0, 0);
240 verts[v + 1].fQuadUV.set(0, -SK_Scalar1);
241 verts[v + 2].fQuadUV.set(0, -SK_Scalar1);
242 verts[v + 3].fQuadUV.set(0, -SK_Scalar1);
243
244 idxs[i + 0] = v + 0;
245 idxs[i + 1] = v + 1;
246 idxs[i + 2] = v + 2;
247 idxs[i + 3] = v + 0;
248 idxs[i + 4] = v + 2;
249 idxs[i + 5] = v + 3;
250
251 v += 4;
252 i += 6;
253
254 if (Segment::kLine == curr.fType) {
255 verts[v + 0].fPos = fanPt;
256 verts[v + 1].fPos = prevPt;
257 verts[v + 2].fPos = curr.fA;
258 verts[v + 3].fPos = prevPt + curr.fANorm;
259 verts[v + 4].fPos = curr.fA + curr.fANorm;
260 GrScalar lineC = -curr.fANorm.dot(curr.fA);
261 GrScalar fanDist = curr.fANorm.dot(fanPt) - lineC;
262 verts[v + 0].fQuadUV.set(0, SkScalarAbs(fanDist));
263 verts[v + 1].fQuadUV.set(0, 0);
264 verts[v + 2].fQuadUV.set(0, 0);
265 verts[v + 3].fQuadUV.set(0, -GR_Scalar1);
266 verts[v + 4].fQuadUV.set(0, -GR_Scalar1);
267
268 idxs[i + 0] = v + 0;
269 idxs[i + 1] = v + 1;
270 idxs[i + 2] = v + 2;
271 idxs[i + 3] = v + 1;
272 idxs[i + 4] = v + 3;
273 idxs[i + 5] = v + 4;
274 idxs[i + 6] = v + 1;
275 idxs[i + 7] = v + 4;
276 idxs[i + 8] = v + 2;
277
278 i += 9;
279 v += 5;
280
281 prevPt = curr.fA;
282 prevNorm = curr.fANorm;
283 } else {
284 GrVec splitVec = curr.fANorm + curr.fBNorm;
285 splitVec.normalize();
286#ifdef STRETCH_AA
287 splitVec.scale(STRETCH_FACTOR);
288#endif
289
290 verts[v + 0].fPos = prevPt;
291 verts[v + 1].fPos = curr.fA;
292 verts[v + 2].fPos = curr.fB;
293 verts[v + 3].fPos = fanPt;
294 verts[v + 4].fPos = prevPt + curr.fANorm;
295 verts[v + 5].fPos = curr.fA + splitVec;
296 verts[v + 6].fPos = curr.fB + curr.fBNorm;
297
298 verts[v + 0].fQuadUV.set(0, 0);
299 verts[v + 1].fQuadUV.set(GR_ScalarHalf, 0);
300 verts[v + 2].fQuadUV.set(GR_Scalar1, GR_Scalar1);
301 GrMatrix toUV;
302 GrPoint pts[] = {prevPt, curr.fA, curr.fB};
303 GrPathUtils::quadDesignSpaceToUVCoordsMatrix(pts, &toUV);
304 toUV.mapPointsWithStride(&verts[v + 3].fQuadUV,
305 &verts[v + 3].fPos,
306 sizeof(QuadVertex), 4);
307
308 idxs[i + 0] = v + 3;
309 idxs[i + 1] = v + 0;
310 idxs[i + 2] = v + 1;
311 idxs[i + 3] = v + 3;
312 idxs[i + 4] = v + 1;
313 idxs[i + 5] = v + 2;
314 idxs[i + 6] = v + 0;
315 idxs[i + 7] = v + 4;
316 idxs[i + 8] = v + 1;
317 idxs[i + 9] = v + 4;
318 idxs[i + 10] = v + 1;
319 idxs[i + 11] = v + 5;
320 idxs[i + 12] = v + 5;
321 idxs[i + 13] = v + 1;
322 idxs[i + 14] = v + 2;
323 idxs[i + 15] = v + 5;
324 idxs[i + 16] = v + 2;
325 idxs[i + 17] = v + 6;
326
327 i += 18;
328 v += 7;
329 prevPt = curr.fB;
330 prevNorm = curr.fBNorm;
331 }
332 }
333}
334
335}
336
337void GrAAConvexPathRenderer::drawPath(GrDrawState::StageMask stageMask) {
338 GrAssert(fPath->isConvex());
339 if (fPath->isEmpty()) {
340 return;
341 }
342 GrDrawState* drawState = fTarget->drawState();
343
344 GrDrawTarget::AutoStateRestore asr;
345 GrMatrix vm = drawState->getViewMatrix();
346 vm.postTranslate(fTranslate.fX, fTranslate.fY);
347 asr.set(fTarget);
348 GrMatrix ivm;
349 if (vm.invert(&ivm)) {
350 drawState->preConcatSamplerMatrices(stageMask, ivm);
351 }
352 drawState->setViewMatrix(GrMatrix::I());
353
354
355 SkPath path;
356 fPath->transform(vm, &path);
357
358 SkPoint fanPt = {path.getBounds().centerX(),
359 path.getBounds().centerY()};
360
361 GrVertexLayout layout = 0;
362 for (int s = 0; s < GrDrawState::kNumStages; ++s) {
363 if ((1 << s) & stageMask) {
364 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
365 }
366 }
367 layout |= GrDrawTarget::kEdge_VertexLayoutBit;
368
369 QuadVertex *verts;
370 uint16_t* idxs;
371
372 int nQuads;
373 int nLines;
374 SegmentArray segments;
375 if (!get_segments(path, &segments, &nQuads, &nLines)) {
376 return;
377 }
378 int vCount;
379 int iCount;
380 get_counts(nQuads, nLines, &vCount, &iCount);
381
382 if (!fTarget->reserveVertexSpace(layout,
383 vCount,
384 reinterpret_cast<void**>(&verts))) {
385 return;
386 }
387 if (!fTarget->reserveIndexSpace(iCount, reinterpret_cast<void**>(&idxs))) {
388 fTarget->resetVertexSource();
389 return;
390 }
391
392 create_vertices(&segments, fanPt, verts, idxs);
393
394 drawState->setVertexEdgeType(GrDrawState::kQuad_EdgeType);
395 fTarget->drawIndexed(kTriangles_PrimitiveType,
396 0, // start vertex
397 0, // start index
398 vCount,
399 iCount);
400}
401