blob: 3b6600421642ba15e016e771d33962211af9c337 [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
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000032struct Segment {
33 enum {
34 kLine,
35 kQuad
36 } fType;
bsalomon@google.com495e2102012-01-21 14:48:36 +000037 // line uses one pt, quad uses 2 pts
38 GrPoint fPts[2];
39 // normal to edge ending at each pt
40 GrVec fNorms[2];
41 // is the corner where the previous segment meets this segment
42 // sharp. If so, fMid is a normalized bisector facing outward.
43 GrVec fMid;
44
45 int countPoints() {
46 return (kLine == fType) ? 1 : 2;
47 }
48 const SkPoint& endPt() const {
49 return (kLine == fType) ? fPts[0] : fPts[1];
50 };
51 const SkPoint& endNorm() const {
52 return (kLine == fType) ? fNorms[0] : fNorms[1];
53 };
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000054};
55
56typedef SkTArray<Segment, true> SegmentArray;
57
58bool is_path_degenerate(const GrPath& path) {
59 int n = path.countPoints();
60 if (n < 3) {
61 return true;
62 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +000063 // compute a line from the first two points that are not equal, look for
64 // a third pt that is off the line.
65 static const SkScalar TOL = (SK_Scalar1 / 16);
66 bool foundLine = false;
67 GrPoint firstPoint = path.getPoint(0);
68 GrVec lineV;
69 SkScalar lineC;
70 int i = 1;
71
72 do {
73 GrPoint pt = path.getPoint(i);
74 if (!foundLine) {
75 if (pt != firstPoint) {
76 lineV = pt - firstPoint;
77 lineV.normalize();
78 lineV.setOrthog(lineV);
79 lineC = lineV.dot(firstPoint);
80 foundLine = true;
81 }
82 } else {
83 if (SkScalarAbs(lineV.dot(pt) - lineC) > TOL) {
84 return false;
85 }
86 }
87 ++i;
88 } while (i < n);
89 return true;
90}
91
bsalomon@google.com495e2102012-01-21 14:48:36 +000092void center_of_mass(const SegmentArray& segments, SkPoint* c) {
93 GrScalar area = 0;
94 SkPoint center;
95 center.set(0, 0);
96 int count = segments.count();
97 for (int i = 0; i < count; ++i) {
98 const SkPoint& pi = segments[i].endPt();
99 int j = (i + 1) % count;
100 const SkPoint& pj = segments[j].endPt();
101 GrScalar t = GrMul(pi.fX, pj.fY) - GrMul(pj.fX, pi.fY);
102 area += t;
103 center.fX += (pi.fX + pj.fX) * t;
104 center.fY += (pi.fY + pj.fY) * t;
105 }
106 area *= 3;
107 area = GrScalarDiv(GR_Scalar1, area);
108 center.fX = GrScalarMul(center.fX, area);
109 center.fY = GrScalarMul(center.fY, area);
110 *c = center;
111}
112
113void compute_vectors(SegmentArray* segments,
114 SkPoint* fanPt,
115 int* vCount,
116 int* iCount) {
117 center_of_mass(*segments, fanPt);
118 int count = segments->count();
119
120 // figure out which way the normals should point
121 GrPoint::Side normSide;
122 fanPt->distanceToLineBetweenSqd((*segments)[0].endPt(),
123 (*segments)[1].endPt(),
124 &normSide);
125
126 *vCount = 0;
127 *iCount = 0;
128 // compute normals at all points
129 for (int a = 0; a < count; ++a) {
130 const Segment& sega = (*segments)[a];
131 int b = (a + 1) % count;
132 Segment& segb = (*segments)[b];
133
134 const GrPoint* prevPt = &sega.endPt();
135 int n = segb.countPoints();
136 for (int p = 0; p < n; ++p) {
137 segb.fNorms[p] = segb.fPts[p] - *prevPt;
138 segb.fNorms[p].normalize();
139 segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
140 prevPt = &segb.fPts[p];
141 }
142 if (Segment::kLine == segb.fType) {
143 *vCount += 5;
144 *iCount += 9;
145 } else {
146 *vCount += 6;
147 *iCount += 12;
148 }
149 }
150
151 // compute mid-vectors where segments meet. TODO: Detect shallow corners
152 // and leave out the wedges and close gaps by stitching segments together.
153 for (int a = 0; a < count; ++a) {
154 const Segment& sega = (*segments)[a];
155 int b = (a + 1) % count;
156 Segment& segb = (*segments)[b];
157 segb.fMid = segb.fNorms[0] + sega.endNorm();
158 segb.fMid.normalize();
159 // corner wedges
160 *vCount += 4;
161 *iCount += 6;
162 }
163}
164
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000165bool get_segments(const GrPath& path,
166 SegmentArray* segments,
bsalomon@google.com495e2102012-01-21 14:48:36 +0000167 SkPoint* fanPt,
168 int* vCount,
169 int* iCount) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000170 SkPath::Iter iter(path, true);
bsalomon@google.com5cc90d12012-01-17 16:28:34 +0000171 // This renderer overemphasises very thin path regions. We use the distance
172 // to the path from the sample to compute coverage. Every pixel intersected
173 // by the path will be hit and the maximum distance is sqrt(2)/2. We don't
174 // notice that the sample may be close to a very thin area of the path and
175 // thus should be very light. This is particularly egregious for degenerate
176 // line paths. We detect paths that are very close to a line (zero area) and
177 // draw nothing.
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000178 if (is_path_degenerate(path)) {
179 return false;
180 }
181 for (;;) {
182 GrPoint pts[4];
183 GrPathCmd cmd = (GrPathCmd)iter.next(pts);
184 switch (cmd) {
185 case kLine_PathCmd: {
186 segments->push_back();
187 segments->back().fType = Segment::kLine;
bsalomon@google.com495e2102012-01-21 14:48:36 +0000188 segments->back().fPts[0] = pts[1];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000189 break;
190 }
191 case kQuadratic_PathCmd:
192 segments->push_back();
193 segments->back().fType = Segment::kQuad;
bsalomon@google.com495e2102012-01-21 14:48:36 +0000194 segments->back().fPts[0] = pts[1];
195 segments->back().fPts[1] = pts[2];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000196 break;
197 case kCubic_PathCmd: {
198 SkSTArray<15, SkPoint, true> quads;
199 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, &quads);
200 int count = quads.count();
201 for (int q = 0; q < count; q += 3) {
202 segments->push_back();
203 segments->back().fType = Segment::kQuad;
bsalomon@google.com495e2102012-01-21 14:48:36 +0000204 segments->back().fPts[0] = quads[q + 1];
205 segments->back().fPts[1] = quads[q + 2];
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000206 }
207 break;
208 };
209 case kEnd_PathCmd:
bsalomon@google.com495e2102012-01-21 14:48:36 +0000210 compute_vectors(segments, fanPt, vCount, iCount);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000211 return true;
212 default:
213 break;
214 }
215 }
216}
217
218struct QuadVertex {
219 GrPoint fPos;
bsalomon@google.com495e2102012-01-21 14:48:36 +0000220 GrPoint fUV;
221 GrScalar fD0;
222 GrScalar fD1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000223};
bsalomon@google.com495e2102012-01-21 14:48:36 +0000224
225void create_vertices(const SegmentArray& segments,
226 const SkPoint& fanPt,
227 QuadVertex* verts,
228 uint16_t* idxs) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000229 int v = 0;
230 int i = 0;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000231
bsalomon@google.com495e2102012-01-21 14:48:36 +0000232 int count = segments.count();
233 for (int a = 0; a < count; ++a) {
234 const Segment& sega = segments[a];
235 int b = (a + 1) % count;
236 const Segment& segb = segments[b];
237
238 // FIXME: These tris are inset in the 1 unit arc around the corner
239 verts[v + 0].fPos = sega.endPt();
240 verts[v + 1].fPos = verts[v + 0].fPos + sega.endNorm();
241 verts[v + 2].fPos = verts[v + 0].fPos + segb.fMid;
242 verts[v + 3].fPos = verts[v + 0].fPos + segb.fNorms[0];
243 verts[v + 0].fUV.set(0,0);
244 verts[v + 1].fUV.set(0,-SK_Scalar1);
245 verts[v + 2].fUV.set(0,-SK_Scalar1);
246 verts[v + 3].fUV.set(0,-SK_Scalar1);
247 verts[v + 0].fD0 = verts[v + 0].fD1 = -SK_Scalar1;
248 verts[v + 1].fD0 = verts[v + 1].fD1 = -SK_Scalar1;
249 verts[v + 2].fD0 = verts[v + 2].fD1 = -SK_Scalar1;
250 verts[v + 3].fD0 = verts[v + 3].fD1 = -SK_Scalar1;
251
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000252 idxs[i + 0] = v + 0;
bsalomon@google.com495e2102012-01-21 14:48:36 +0000253 idxs[i + 1] = v + 2;
254 idxs[i + 2] = v + 1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000255 idxs[i + 3] = v + 0;
bsalomon@google.com495e2102012-01-21 14:48:36 +0000256 idxs[i + 4] = v + 3;
257 idxs[i + 5] = v + 2;
258
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000259 v += 4;
260 i += 6;
261
bsalomon@google.com495e2102012-01-21 14:48:36 +0000262 if (Segment::kLine == segb.fType) {
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000263 verts[v + 0].fPos = fanPt;
bsalomon@google.com495e2102012-01-21 14:48:36 +0000264 verts[v + 1].fPos = sega.endPt();
265 verts[v + 2].fPos = segb.fPts[0];
266
267 verts[v + 3].fPos = verts[v + 1].fPos + segb.fNorms[0];
268 verts[v + 4].fPos = verts[v + 2].fPos + segb.fNorms[0];
269
270 // we draw the line edge as a degenerate quad (u is 0, v is the
271 // signed distance to the edge)
272 GrScalar dist = fanPt.distanceToLineBetween(verts[v + 1].fPos,
273 verts[v + 2].fPos);
274 verts[v + 0].fUV.set(0, dist);
275 verts[v + 1].fUV.set(0, 0);
276 verts[v + 2].fUV.set(0, 0);
277 verts[v + 3].fUV.set(0, -SK_Scalar1);
278 verts[v + 4].fUV.set(0, -SK_Scalar1);
279
280 verts[v + 0].fD0 = verts[v + 0].fD1 = -SK_Scalar1;
281 verts[v + 1].fD0 = verts[v + 1].fD1 = -SK_Scalar1;
282 verts[v + 2].fD0 = verts[v + 2].fD1 = -SK_Scalar1;
283 verts[v + 3].fD0 = verts[v + 3].fD1 = -SK_Scalar1;
284 verts[v + 4].fD0 = verts[v + 4].fD1 = -SK_Scalar1;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000285
286 idxs[i + 0] = v + 0;
bsalomon@google.com495e2102012-01-21 14:48:36 +0000287 idxs[i + 1] = v + 2;
288 idxs[i + 2] = v + 1;
289
290 idxs[i + 3] = v + 3;
291 idxs[i + 4] = v + 1;
292 idxs[i + 5] = v + 2;
293
294 idxs[i + 6] = v + 4;
295 idxs[i + 7] = v + 3;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000296 idxs[i + 8] = v + 2;
297
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000298 v += 5;
bsalomon@google.com495e2102012-01-21 14:48:36 +0000299 i += 9;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000300 } else {
bsalomon@google.com495e2102012-01-21 14:48:36 +0000301 GrPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000302
bsalomon@google.com495e2102012-01-21 14:48:36 +0000303 GrVec midVec = segb.fNorms[0] + segb.fNorms[1];
304 midVec.normalize();
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000305
bsalomon@google.com495e2102012-01-21 14:48:36 +0000306 verts[v + 0].fPos = fanPt;
307 verts[v + 1].fPos = qpts[0];
308 verts[v + 2].fPos = qpts[2];
309 verts[v + 3].fPos = qpts[0] + segb.fNorms[0];
310 verts[v + 4].fPos = qpts[2] + segb.fNorms[1];
311 verts[v + 5].fPos = qpts[1] + midVec;
312
313 GrScalar c = segb.fNorms[0].dot(qpts[0]);
314 verts[v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
315 verts[v + 1].fD0 = 0.f;
316 verts[v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
317 verts[v + 3].fD0 = -GR_ScalarMax/100;
318 verts[v + 4].fD0 = -GR_ScalarMax/100;
319 verts[v + 5].fD0 = -GR_ScalarMax/100;
320
321 c = segb.fNorms[1].dot(qpts[2]);
322 verts[v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
323 verts[v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
324 verts[v + 2].fD1 = 0.f;
325 verts[v + 3].fD1 = -GR_ScalarMax/100;
326 verts[v + 4].fD1 = -GR_ScalarMax/100;
327 verts[v + 5].fD1 = -GR_ScalarMax/100;
328
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000329 GrMatrix toUV;
bsalomon@google.com495e2102012-01-21 14:48:36 +0000330 GrPathUtils::quadDesignSpaceToUVCoordsMatrix(qpts, &toUV);
331 toUV.mapPointsWithStride(&verts[v].fUV,
332 &verts[v].fPos,
333 sizeof(QuadVertex),
334 6);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000335
bsalomon@google.com495e2102012-01-21 14:48:36 +0000336 idxs[i + 0] = v + 3;
337 idxs[i + 1] = v + 1;
338 idxs[i + 2] = v + 2;
339 idxs[i + 3] = v + 4;
340 idxs[i + 4] = v + 3;
341 idxs[i + 5] = v + 2;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000342
bsalomon@google.com495e2102012-01-21 14:48:36 +0000343 idxs[i + 6] = v + 5;
344 idxs[i + 7] = v + 3;
345 idxs[i + 8] = v + 4;
346
347 idxs[i + 9] = v + 0;
348 idxs[i + 10] = v + 2;
349 idxs[i + 11] = v + 1;
350
351 v += 6;
352 i += 12;
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000353 }
354 }
355}
356
357}
358
359void GrAAConvexPathRenderer::drawPath(GrDrawState::StageMask stageMask) {
360 GrAssert(fPath->isConvex());
361 if (fPath->isEmpty()) {
362 return;
363 }
364 GrDrawState* drawState = fTarget->drawState();
365
366 GrDrawTarget::AutoStateRestore asr;
367 GrMatrix vm = drawState->getViewMatrix();
368 vm.postTranslate(fTranslate.fX, fTranslate.fY);
369 asr.set(fTarget);
370 GrMatrix ivm;
371 if (vm.invert(&ivm)) {
372 drawState->preConcatSamplerMatrices(stageMask, ivm);
373 }
374 drawState->setViewMatrix(GrMatrix::I());
375
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000376 SkPath path;
377 fPath->transform(vm, &path);
378
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000379 GrVertexLayout layout = 0;
380 for (int s = 0; s < GrDrawState::kNumStages; ++s) {
381 if ((1 << s) & stageMask) {
382 layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
383 }
384 }
385 layout |= GrDrawTarget::kEdge_VertexLayoutBit;
386
387 QuadVertex *verts;
388 uint16_t* idxs;
389
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000390 int vCount;
391 int iCount;
bsalomon@google.com495e2102012-01-21 14:48:36 +0000392 SegmentArray segments;
393 SkPoint fanPt;
394 if (!get_segments(path, &segments, &fanPt, &vCount, &iCount)) {
395 return;
396 }
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000397
398 if (!fTarget->reserveVertexSpace(layout,
399 vCount,
400 reinterpret_cast<void**>(&verts))) {
401 return;
402 }
403 if (!fTarget->reserveIndexSpace(iCount, reinterpret_cast<void**>(&idxs))) {
404 fTarget->resetVertexSource();
405 return;
406 }
407
bsalomon@google.com495e2102012-01-21 14:48:36 +0000408 create_vertices(segments, fanPt, verts, idxs);
bsalomon@google.com69cc6ad2012-01-17 14:25:10 +0000409
410 drawState->setVertexEdgeType(GrDrawState::kQuad_EdgeType);
411 fTarget->drawIndexed(kTriangles_PrimitiveType,
412 0, // start vertex
413 0, // start index
414 vCount,
415 iCount);
416}
417