1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "GrAAHairLinePathRenderer.h"
9 
10 #include "GrBatch.h"
11 #include "GrBatchTarget.h"
12 #include "GrBatchTest.h"
13 #include "GrContext.h"
14 #include "GrDefaultGeoProcFactory.h"
15 #include "GrDrawTargetCaps.h"
16 #include "GrIndexBuffer.h"
17 #include "GrPathUtils.h"
18 #include "GrPipelineBuilder.h"
19 #include "GrProcessor.h"
20 #include "GrResourceProvider.h"
21 #include "GrVertexBuffer.h"
22 #include "SkGeometry.h"
23 #include "SkStroke.h"
24 #include "SkTemplates.h"
25 
26 #include "effects/GrBezierEffect.h"
27 
28 #define PREALLOC_PTARRAY(N) SkSTArray<(N),SkPoint, true>
29 
30 // quadratics are rendered as 5-sided polys in order to bound the
31 // AA stroke around the center-curve. See comments in push_quad_index_buffer and
32 // bloat_quad. Quadratics and conics share an index buffer
33 
34 // lines are rendered as:
35 //      *______________*
36 //      |\ -_______   /|
37 //      | \        \ / |
38 //      |  *--------*  |
39 //      | /  ______/ \ |
40 //      */_-__________\*
41 // For: 6 vertices and 18 indices (for 6 triangles)
42 
43 // Each quadratic is rendered as a five sided polygon. This poly bounds
44 // the quadratic's bounding triangle but has been expanded so that the
45 // 1-pixel wide area around the curve is inside the poly.
46 // If a,b,c are the original control points then the poly a0,b0,c0,c1,a1
47 // that is rendered would look like this:
48 //              b0
49 //              b
50 //
51 //     a0              c0
52 //      a            c
53 //       a1       c1
54 // Each is drawn as three triangles ((a0,a1,b0), (b0,c1,c0), (a1,c1,b0))
55 // specified by these 9 indices:
56 static const uint16_t kQuadIdxBufPattern[] = {
57     0, 1, 2,
58     2, 4, 3,
59     1, 4, 2
60 };
61 
62 static const int kIdxsPerQuad = SK_ARRAY_COUNT(kQuadIdxBufPattern);
63 static const int kQuadNumVertices = 5;
64 static const int kQuadsNumInIdxBuffer = 256;
65 GR_DECLARE_STATIC_UNIQUE_KEY(gQuadsIndexBufferKey);
66 
ref_quads_index_buffer(GrResourceProvider * resourceProvider)67 static const GrIndexBuffer* ref_quads_index_buffer(GrResourceProvider* resourceProvider) {
68     GR_DEFINE_STATIC_UNIQUE_KEY(gQuadsIndexBufferKey);
69     return resourceProvider->refOrCreateInstancedIndexBuffer(
70         kQuadIdxBufPattern, kIdxsPerQuad, kQuadsNumInIdxBuffer, kQuadNumVertices,
71         gQuadsIndexBufferKey);
72 }
73 
74 
75 // Each line segment is rendered as two quads and two triangles.
76 // p0 and p1 have alpha = 1 while all other points have alpha = 0.
77 // The four external points are offset 1 pixel perpendicular to the
78 // line and half a pixel parallel to the line.
79 //
80 // p4                  p5
81 //      p0         p1
82 // p2                  p3
83 //
84 // Each is drawn as six triangles specified by these 18 indices:
85 
86 static const uint16_t kLineSegIdxBufPattern[] = {
87     0, 1, 3,
88     0, 3, 2,
89     0, 4, 5,
90     0, 5, 1,
91     0, 2, 4,
92     1, 5, 3
93 };
94 
95 static const int kIdxsPerLineSeg = SK_ARRAY_COUNT(kLineSegIdxBufPattern);
96 static const int kLineSegNumVertices = 6;
97 static const int kLineSegsNumInIdxBuffer = 256;
98 
99 GR_DECLARE_STATIC_UNIQUE_KEY(gLinesIndexBufferKey);
100 
ref_lines_index_buffer(GrResourceProvider * resourceProvider)101 static const GrIndexBuffer* ref_lines_index_buffer(GrResourceProvider* resourceProvider) {
102     GR_DEFINE_STATIC_UNIQUE_KEY(gLinesIndexBufferKey);
103     return resourceProvider->refOrCreateInstancedIndexBuffer(
104         kLineSegIdxBufPattern, kIdxsPerLineSeg,  kLineSegsNumInIdxBuffer, kLineSegNumVertices,
105         gLinesIndexBufferKey);
106 }
107 
108 // Takes 178th time of logf on Z600 / VC2010
get_float_exp(float x)109 static int get_float_exp(float x) {
110     GR_STATIC_ASSERT(sizeof(int) == sizeof(float));
111 #ifdef SK_DEBUG
112     static bool tested;
113     if (!tested) {
114         tested = true;
115         SkASSERT(get_float_exp(0.25f) == -2);
116         SkASSERT(get_float_exp(0.3f) == -2);
117         SkASSERT(get_float_exp(0.5f) == -1);
118         SkASSERT(get_float_exp(1.f) == 0);
119         SkASSERT(get_float_exp(2.f) == 1);
120         SkASSERT(get_float_exp(2.5f) == 1);
121         SkASSERT(get_float_exp(8.f) == 3);
122         SkASSERT(get_float_exp(100.f) == 6);
123         SkASSERT(get_float_exp(1000.f) == 9);
124         SkASSERT(get_float_exp(1024.f) == 10);
125         SkASSERT(get_float_exp(3000000.f) == 21);
126     }
127 #endif
128     const int* iptr = (const int*)&x;
129     return (((*iptr) & 0x7f800000) >> 23) - 127;
130 }
131 
132 // Uses the max curvature function for quads to estimate
133 // where to chop the conic. If the max curvature is not
134 // found along the curve segment it will return 1 and
135 // dst[0] is the original conic. If it returns 2 the dst[0]
136 // and dst[1] are the two new conics.
split_conic(const SkPoint src[3],SkConic dst[2],const SkScalar weight)137 static int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
138     SkScalar t = SkFindQuadMaxCurvature(src);
139     if (t == 0) {
140         if (dst) {
141             dst[0].set(src, weight);
142         }
143         return 1;
144     } else {
145         if (dst) {
146             SkConic conic;
147             conic.set(src, weight);
148             conic.chopAt(t, dst);
149         }
150         return 2;
151     }
152 }
153 
154 // Calls split_conic on the entire conic and then once more on each subsection.
155 // Most cases will result in either 1 conic (chop point is not within t range)
156 // or 3 points (split once and then one subsection is split again).
chop_conic(const SkPoint src[3],SkConic dst[4],const SkScalar weight)157 static int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
158     SkConic dstTemp[2];
159     int conicCnt = split_conic(src, dstTemp, weight);
160     if (2 == conicCnt) {
161         int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
162         conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
163     } else {
164         dst[0] = dstTemp[0];
165     }
166     return conicCnt;
167 }
168 
169 // returns 0 if quad/conic is degen or close to it
170 // in this case approx the path with lines
171 // otherwise returns 1
is_degen_quad_or_conic(const SkPoint p[3],SkScalar * dsqd)172 static int is_degen_quad_or_conic(const SkPoint p[3], SkScalar* dsqd) {
173     static const SkScalar gDegenerateToLineTol = GrPathUtils::kDefaultTolerance;
174     static const SkScalar gDegenerateToLineTolSqd =
175         SkScalarMul(gDegenerateToLineTol, gDegenerateToLineTol);
176 
177     if (p[0].distanceToSqd(p[1]) < gDegenerateToLineTolSqd ||
178         p[1].distanceToSqd(p[2]) < gDegenerateToLineTolSqd) {
179         return 1;
180     }
181 
182     *dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]);
183     if (*dsqd < gDegenerateToLineTolSqd) {
184         return 1;
185     }
186 
187     if (p[2].distanceToLineBetweenSqd(p[1], p[0]) < gDegenerateToLineTolSqd) {
188         return 1;
189     }
190     return 0;
191 }
192 
is_degen_quad_or_conic(const SkPoint p[3])193 static int is_degen_quad_or_conic(const SkPoint p[3]) {
194     SkScalar dsqd;
195     return is_degen_quad_or_conic(p, &dsqd);
196 }
197 
198 // we subdivide the quads to avoid huge overfill
199 // if it returns -1 then should be drawn as lines
num_quad_subdivs(const SkPoint p[3])200 static int num_quad_subdivs(const SkPoint p[3]) {
201     SkScalar dsqd;
202     if (is_degen_quad_or_conic(p, &dsqd)) {
203         return -1;
204     }
205 
206     // tolerance of triangle height in pixels
207     // tuned on windows  Quadro FX 380 / Z600
208     // trade off of fill vs cpu time on verts
209     // maybe different when do this using gpu (geo or tess shaders)
210     static const SkScalar gSubdivTol = 175 * SK_Scalar1;
211 
212     if (dsqd <= SkScalarMul(gSubdivTol, gSubdivTol)) {
213         return 0;
214     } else {
215         static const int kMaxSub = 4;
216         // subdividing the quad reduces d by 4. so we want x = log4(d/tol)
217         // = log4(d*d/tol*tol)/2
218         // = log2(d*d/tol*tol)
219 
220         // +1 since we're ignoring the mantissa contribution.
221         int log = get_float_exp(dsqd/(gSubdivTol*gSubdivTol)) + 1;
222         log = SkTMin(SkTMax(0, log), kMaxSub);
223         return log;
224     }
225 }
226 
227 /**
228  * Generates the lines and quads to be rendered. Lines are always recorded in
229  * device space. We will do a device space bloat to account for the 1pixel
230  * thickness.
231  * Quads are recorded in device space unless m contains
232  * perspective, then in they are in src space. We do this because we will
233  * subdivide large quads to reduce over-fill. This subdivision has to be
234  * performed before applying the perspective matrix.
235  */
gather_lines_and_quads(const SkPath & path,const SkMatrix & m,const SkIRect & devClipBounds,GrAAHairLinePathRenderer::PtArray * lines,GrAAHairLinePathRenderer::PtArray * quads,GrAAHairLinePathRenderer::PtArray * conics,GrAAHairLinePathRenderer::IntArray * quadSubdivCnts,GrAAHairLinePathRenderer::FloatArray * conicWeights)236 static int gather_lines_and_quads(const SkPath& path,
237                                   const SkMatrix& m,
238                                   const SkIRect& devClipBounds,
239                                   GrAAHairLinePathRenderer::PtArray* lines,
240                                   GrAAHairLinePathRenderer::PtArray* quads,
241                                   GrAAHairLinePathRenderer::PtArray* conics,
242                                   GrAAHairLinePathRenderer::IntArray* quadSubdivCnts,
243                                   GrAAHairLinePathRenderer::FloatArray* conicWeights) {
244     SkPath::Iter iter(path, false);
245 
246     int totalQuadCount = 0;
247     SkRect bounds;
248     SkIRect ibounds;
249 
250     bool persp = m.hasPerspective();
251 
252     for (;;) {
253         SkPoint pathPts[4];
254         SkPoint devPts[4];
255         SkPath::Verb verb = iter.next(pathPts);
256         switch (verb) {
257             case SkPath::kConic_Verb: {
258                 SkConic dst[4];
259                 // We chop the conics to create tighter clipping to hide error
260                 // that appears near max curvature of very thin conics. Thin
261                 // hyperbolas with high weight still show error.
262                 int conicCnt = chop_conic(pathPts, dst, iter.conicWeight());
263                 for (int i = 0; i < conicCnt; ++i) {
264                     SkPoint* chopPnts = dst[i].fPts;
265                     m.mapPoints(devPts, chopPnts, 3);
266                     bounds.setBounds(devPts, 3);
267                     bounds.outset(SK_Scalar1, SK_Scalar1);
268                     bounds.roundOut(&ibounds);
269                     if (SkIRect::Intersects(devClipBounds, ibounds)) {
270                         if (is_degen_quad_or_conic(devPts)) {
271                             SkPoint* pts = lines->push_back_n(4);
272                             pts[0] = devPts[0];
273                             pts[1] = devPts[1];
274                             pts[2] = devPts[1];
275                             pts[3] = devPts[2];
276                         } else {
277                             // when in perspective keep conics in src space
278                             SkPoint* cPts = persp ? chopPnts : devPts;
279                             SkPoint* pts = conics->push_back_n(3);
280                             pts[0] = cPts[0];
281                             pts[1] = cPts[1];
282                             pts[2] = cPts[2];
283                             conicWeights->push_back() = dst[i].fW;
284                         }
285                     }
286                 }
287                 break;
288             }
289             case SkPath::kMove_Verb:
290                 break;
291             case SkPath::kLine_Verb:
292                 m.mapPoints(devPts, pathPts, 2);
293                 bounds.setBounds(devPts, 2);
294                 bounds.outset(SK_Scalar1, SK_Scalar1);
295                 bounds.roundOut(&ibounds);
296                 if (SkIRect::Intersects(devClipBounds, ibounds)) {
297                     SkPoint* pts = lines->push_back_n(2);
298                     pts[0] = devPts[0];
299                     pts[1] = devPts[1];
300                 }
301                 break;
302             case SkPath::kQuad_Verb: {
303                 SkPoint choppedPts[5];
304                 // Chopping the quad helps when the quad is either degenerate or nearly degenerate.
305                 // When it is degenerate it allows the approximation with lines to work since the
306                 // chop point (if there is one) will be at the parabola's vertex. In the nearly
307                 // degenerate the QuadUVMatrix computed for the points is almost singular which
308                 // can cause rendering artifacts.
309                 int n = SkChopQuadAtMaxCurvature(pathPts, choppedPts);
310                 for (int i = 0; i < n; ++i) {
311                     SkPoint* quadPts = choppedPts + i * 2;
312                     m.mapPoints(devPts, quadPts, 3);
313                     bounds.setBounds(devPts, 3);
314                     bounds.outset(SK_Scalar1, SK_Scalar1);
315                     bounds.roundOut(&ibounds);
316 
317                     if (SkIRect::Intersects(devClipBounds, ibounds)) {
318                         int subdiv = num_quad_subdivs(devPts);
319                         SkASSERT(subdiv >= -1);
320                         if (-1 == subdiv) {
321                             SkPoint* pts = lines->push_back_n(4);
322                             pts[0] = devPts[0];
323                             pts[1] = devPts[1];
324                             pts[2] = devPts[1];
325                             pts[3] = devPts[2];
326                         } else {
327                             // when in perspective keep quads in src space
328                             SkPoint* qPts = persp ? quadPts : devPts;
329                             SkPoint* pts = quads->push_back_n(3);
330                             pts[0] = qPts[0];
331                             pts[1] = qPts[1];
332                             pts[2] = qPts[2];
333                             quadSubdivCnts->push_back() = subdiv;
334                             totalQuadCount += 1 << subdiv;
335                         }
336                     }
337                 }
338                 break;
339             }
340             case SkPath::kCubic_Verb:
341                 m.mapPoints(devPts, pathPts, 4);
342                 bounds.setBounds(devPts, 4);
343                 bounds.outset(SK_Scalar1, SK_Scalar1);
344                 bounds.roundOut(&ibounds);
345                 if (SkIRect::Intersects(devClipBounds, ibounds)) {
346                     PREALLOC_PTARRAY(32) q;
347                     // we don't need a direction if we aren't constraining the subdivision
348                     static const SkPath::Direction kDummyDir = SkPath::kCCW_Direction;
349                     // We convert cubics to quadratics (for now).
350                     // In perspective have to do conversion in src space.
351                     if (persp) {
352                         SkScalar tolScale =
353                             GrPathUtils::scaleToleranceToSrc(SK_Scalar1, m,
354                                                              path.getBounds());
355                         GrPathUtils::convertCubicToQuads(pathPts, tolScale, false, kDummyDir, &q);
356                     } else {
357                         GrPathUtils::convertCubicToQuads(devPts, SK_Scalar1, false, kDummyDir, &q);
358                     }
359                     for (int i = 0; i < q.count(); i += 3) {
360                         SkPoint* qInDevSpace;
361                         // bounds has to be calculated in device space, but q is
362                         // in src space when there is perspective.
363                         if (persp) {
364                             m.mapPoints(devPts, &q[i], 3);
365                             bounds.setBounds(devPts, 3);
366                             qInDevSpace = devPts;
367                         } else {
368                             bounds.setBounds(&q[i], 3);
369                             qInDevSpace = &q[i];
370                         }
371                         bounds.outset(SK_Scalar1, SK_Scalar1);
372                         bounds.roundOut(&ibounds);
373                         if (SkIRect::Intersects(devClipBounds, ibounds)) {
374                             int subdiv = num_quad_subdivs(qInDevSpace);
375                             SkASSERT(subdiv >= -1);
376                             if (-1 == subdiv) {
377                                 SkPoint* pts = lines->push_back_n(4);
378                                 // lines should always be in device coords
379                                 pts[0] = qInDevSpace[0];
380                                 pts[1] = qInDevSpace[1];
381                                 pts[2] = qInDevSpace[1];
382                                 pts[3] = qInDevSpace[2];
383                             } else {
384                                 SkPoint* pts = quads->push_back_n(3);
385                                 // q is already in src space when there is no
386                                 // perspective and dev coords otherwise.
387                                 pts[0] = q[0 + i];
388                                 pts[1] = q[1 + i];
389                                 pts[2] = q[2 + i];
390                                 quadSubdivCnts->push_back() = subdiv;
391                                 totalQuadCount += 1 << subdiv;
392                             }
393                         }
394                     }
395                 }
396                 break;
397             case SkPath::kClose_Verb:
398                 break;
399             case SkPath::kDone_Verb:
400                 return totalQuadCount;
401         }
402     }
403 }
404 
405 struct LineVertex {
406     SkPoint fPos;
407     float fCoverage;
408 };
409 
410 struct BezierVertex {
411     SkPoint fPos;
412     union {
413         struct {
414             SkScalar fK;
415             SkScalar fL;
416             SkScalar fM;
417         } fConic;
418         SkVector   fQuadCoord;
419         struct {
420             SkScalar fBogus[4];
421         };
422     };
423 };
424 
425 GR_STATIC_ASSERT(sizeof(BezierVertex) == 3 * sizeof(SkPoint));
426 
intersect_lines(const SkPoint & ptA,const SkVector & normA,const SkPoint & ptB,const SkVector & normB,SkPoint * result)427 static void intersect_lines(const SkPoint& ptA, const SkVector& normA,
428                             const SkPoint& ptB, const SkVector& normB,
429                             SkPoint* result) {
430 
431     SkScalar lineAW = -normA.dot(ptA);
432     SkScalar lineBW = -normB.dot(ptB);
433 
434     SkScalar wInv = SkScalarMul(normA.fX, normB.fY) -
435         SkScalarMul(normA.fY, normB.fX);
436     wInv = SkScalarInvert(wInv);
437 
438     result->fX = SkScalarMul(normA.fY, lineBW) - SkScalarMul(lineAW, normB.fY);
439     result->fX = SkScalarMul(result->fX, wInv);
440 
441     result->fY = SkScalarMul(lineAW, normB.fX) - SkScalarMul(normA.fX, lineBW);
442     result->fY = SkScalarMul(result->fY, wInv);
443 }
444 
set_uv_quad(const SkPoint qpts[3],BezierVertex verts[kQuadNumVertices])445 static void set_uv_quad(const SkPoint qpts[3], BezierVertex verts[kQuadNumVertices]) {
446     // this should be in the src space, not dev coords, when we have perspective
447     GrPathUtils::QuadUVMatrix DevToUV(qpts);
448     DevToUV.apply<kQuadNumVertices, sizeof(BezierVertex), sizeof(SkPoint)>(verts);
449 }
450 
bloat_quad(const SkPoint qpts[3],const SkMatrix * toDevice,const SkMatrix * toSrc,BezierVertex verts[kQuadNumVertices])451 static void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice,
452                        const SkMatrix* toSrc, BezierVertex verts[kQuadNumVertices]) {
453     SkASSERT(!toDevice == !toSrc);
454     // original quad is specified by tri a,b,c
455     SkPoint a = qpts[0];
456     SkPoint b = qpts[1];
457     SkPoint c = qpts[2];
458 
459     if (toDevice) {
460         toDevice->mapPoints(&a, 1);
461         toDevice->mapPoints(&b, 1);
462         toDevice->mapPoints(&c, 1);
463     }
464     // make a new poly where we replace a and c by a 1-pixel wide edges orthog
465     // to edges ab and bc:
466     //
467     //   before       |        after
468     //                |              b0
469     //         b      |
470     //                |
471     //                |     a0            c0
472     // a         c    |        a1       c1
473     //
474     // edges a0->b0 and b0->c0 are parallel to original edges a->b and b->c,
475     // respectively.
476     BezierVertex& a0 = verts[0];
477     BezierVertex& a1 = verts[1];
478     BezierVertex& b0 = verts[2];
479     BezierVertex& c0 = verts[3];
480     BezierVertex& c1 = verts[4];
481 
482     SkVector ab = b;
483     ab -= a;
484     SkVector ac = c;
485     ac -= a;
486     SkVector cb = b;
487     cb -= c;
488 
489     // We should have already handled degenerates
490     SkASSERT(ab.length() > 0 && cb.length() > 0);
491 
492     ab.normalize();
493     SkVector abN;
494     abN.setOrthog(ab, SkVector::kLeft_Side);
495     if (abN.dot(ac) > 0) {
496         abN.negate();
497     }
498 
499     cb.normalize();
500     SkVector cbN;
501     cbN.setOrthog(cb, SkVector::kLeft_Side);
502     if (cbN.dot(ac) < 0) {
503         cbN.negate();
504     }
505 
506     a0.fPos = a;
507     a0.fPos += abN;
508     a1.fPos = a;
509     a1.fPos -= abN;
510 
511     c0.fPos = c;
512     c0.fPos += cbN;
513     c1.fPos = c;
514     c1.fPos -= cbN;
515 
516     intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos);
517 
518     if (toSrc) {
519         toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(BezierVertex), kQuadNumVertices);
520     }
521 }
522 
523 // Equations based off of Loop-Blinn Quadratic GPU Rendering
524 // Input Parametric:
525 // P(t) = (P0*(1-t)^2 + 2*w*P1*t*(1-t) + P2*t^2) / (1-t)^2 + 2*w*t*(1-t) + t^2)
526 // Output Implicit:
527 // f(x, y, w) = f(P) = K^2 - LM
528 // K = dot(k, P), L = dot(l, P), M = dot(m, P)
529 // k, l, m are calculated in function GrPathUtils::getConicKLM
set_conic_coeffs(const SkPoint p[3],BezierVertex verts[kQuadNumVertices],const SkScalar weight)530 static void set_conic_coeffs(const SkPoint p[3], BezierVertex verts[kQuadNumVertices],
531                              const SkScalar weight) {
532     SkScalar klm[9];
533 
534     GrPathUtils::getConicKLM(p, weight, klm);
535 
536     for (int i = 0; i < kQuadNumVertices; ++i) {
537         const SkPoint pnt = verts[i].fPos;
538         verts[i].fConic.fK = pnt.fX * klm[0] + pnt.fY * klm[1] + klm[2];
539         verts[i].fConic.fL = pnt.fX * klm[3] + pnt.fY * klm[4] + klm[5];
540         verts[i].fConic.fM = pnt.fX * klm[6] + pnt.fY * klm[7] + klm[8];
541     }
542 }
543 
add_conics(const SkPoint p[3],const SkScalar weight,const SkMatrix * toDevice,const SkMatrix * toSrc,BezierVertex ** vert)544 static void add_conics(const SkPoint p[3],
545                        const SkScalar weight,
546                        const SkMatrix* toDevice,
547                        const SkMatrix* toSrc,
548                        BezierVertex** vert) {
549     bloat_quad(p, toDevice, toSrc, *vert);
550     set_conic_coeffs(p, *vert, weight);
551     *vert += kQuadNumVertices;
552 }
553 
add_quads(const SkPoint p[3],int subdiv,const SkMatrix * toDevice,const SkMatrix * toSrc,BezierVertex ** vert)554 static void add_quads(const SkPoint p[3],
555                       int subdiv,
556                       const SkMatrix* toDevice,
557                       const SkMatrix* toSrc,
558                       BezierVertex** vert) {
559     SkASSERT(subdiv >= 0);
560     if (subdiv) {
561         SkPoint newP[5];
562         SkChopQuadAtHalf(p, newP);
563         add_quads(newP + 0, subdiv-1, toDevice, toSrc, vert);
564         add_quads(newP + 2, subdiv-1, toDevice, toSrc, vert);
565     } else {
566         bloat_quad(p, toDevice, toSrc, *vert);
567         set_uv_quad(p, *vert);
568         *vert += kQuadNumVertices;
569     }
570 }
571 
add_line(const SkPoint p[2],const SkMatrix * toSrc,uint8_t coverage,LineVertex ** vert)572 static void add_line(const SkPoint p[2],
573                      const SkMatrix* toSrc,
574                      uint8_t coverage,
575                      LineVertex** vert) {
576     const SkPoint& a = p[0];
577     const SkPoint& b = p[1];
578 
579     SkVector ortho, vec = b;
580     vec -= a;
581 
582     if (vec.setLength(SK_ScalarHalf)) {
583         // Create a vector orthogonal to 'vec' and of unit length
584         ortho.fX = 2.0f * vec.fY;
585         ortho.fY = -2.0f * vec.fX;
586 
587         float floatCoverage = GrNormalizeByteToFloat(coverage);
588 
589         (*vert)[0].fPos = a;
590         (*vert)[0].fCoverage = floatCoverage;
591         (*vert)[1].fPos = b;
592         (*vert)[1].fCoverage = floatCoverage;
593         (*vert)[2].fPos = a - vec + ortho;
594         (*vert)[2].fCoverage = 0;
595         (*vert)[3].fPos = b + vec + ortho;
596         (*vert)[3].fCoverage = 0;
597         (*vert)[4].fPos = a - vec - ortho;
598         (*vert)[4].fCoverage = 0;
599         (*vert)[5].fPos = b + vec - ortho;
600         (*vert)[5].fCoverage = 0;
601 
602         if (toSrc) {
603             toSrc->mapPointsWithStride(&(*vert)->fPos,
604                                        sizeof(LineVertex),
605                                        kLineSegNumVertices);
606         }
607     } else {
608         // just make it degenerate and likely offscreen
609         for (int i = 0; i < kLineSegNumVertices; ++i) {
610             (*vert)[i].fPos.set(SK_ScalarMax, SK_ScalarMax);
611         }
612     }
613 
614     *vert += kLineSegNumVertices;
615 }
616 
617 ///////////////////////////////////////////////////////////////////////////////
618 
canDrawPath(const GrDrawTarget * target,const GrPipelineBuilder * pipelineBuilder,const SkMatrix & viewMatrix,const SkPath & path,const GrStrokeInfo & stroke,bool antiAlias) const619 bool GrAAHairLinePathRenderer::canDrawPath(const GrDrawTarget* target,
620                                            const GrPipelineBuilder* pipelineBuilder,
621                                            const SkMatrix& viewMatrix,
622                                            const SkPath& path,
623                                            const GrStrokeInfo& stroke,
624                                            bool antiAlias) const {
625     if (!antiAlias) {
626         return false;
627     }
628 
629     if (!IsStrokeHairlineOrEquivalent(stroke, viewMatrix, NULL)) {
630         return false;
631     }
632 
633     if (SkPath::kLine_SegmentMask == path.getSegmentMasks() ||
634         target->caps()->shaderCaps()->shaderDerivativeSupport()) {
635         return true;
636     }
637     return false;
638 }
639 
640 template <class VertexType>
check_bounds(const SkMatrix & viewMatrix,const SkRect & devBounds,void * vertices,int vCount)641 bool check_bounds(const SkMatrix& viewMatrix, const SkRect& devBounds, void* vertices, int vCount)
642 {
643     SkRect tolDevBounds = devBounds;
644     // The bounds ought to be tight, but in perspective the below code runs the verts
645     // through the view matrix to get back to dev coords, which can introduce imprecision.
646     if (viewMatrix.hasPerspective()) {
647         tolDevBounds.outset(SK_Scalar1 / 1000, SK_Scalar1 / 1000);
648     } else {
649         // Non-persp matrices cause this path renderer to draw in device space.
650         SkASSERT(viewMatrix.isIdentity());
651     }
652     SkRect actualBounds;
653 
654     VertexType* verts = reinterpret_cast<VertexType*>(vertices);
655     bool first = true;
656     for (int i = 0; i < vCount; ++i) {
657         SkPoint pos = verts[i].fPos;
658         // This is a hack to workaround the fact that we move some degenerate segments offscreen.
659         if (SK_ScalarMax == pos.fX) {
660             continue;
661         }
662         viewMatrix.mapPoints(&pos, 1);
663         if (first) {
664             actualBounds.set(pos.fX, pos.fY, pos.fX, pos.fY);
665             first = false;
666         } else {
667             actualBounds.growToInclude(pos.fX, pos.fY);
668         }
669     }
670     if (!first) {
671         return tolDevBounds.contains(actualBounds);
672     }
673 
674     return true;
675 }
676 
677 class AAHairlineBatch : public GrBatch {
678 public:
679     struct Geometry {
680         GrColor fColor;
681         uint8_t fCoverage;
682         SkMatrix fViewMatrix;
683         SkPath fPath;
684         SkIRect fDevClipBounds;
685     };
686 
Create(const Geometry & geometry)687     static GrBatch* Create(const Geometry& geometry) {
688         return SkNEW_ARGS(AAHairlineBatch, (geometry));
689     }
690 
name() const691     const char* name() const override { return "AAHairlineBatch"; }
692 
getInvariantOutputColor(GrInitInvariantOutput * out) const693     void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
694         // When this is called on a batch, there is only one geometry bundle
695         out->setKnownFourComponents(fGeoData[0].fColor);
696     }
getInvariantOutputCoverage(GrInitInvariantOutput * out) const697     void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
698         out->setUnknownSingleComponent();
699     }
700 
initBatchTracker(const GrPipelineInfo & init)701     void initBatchTracker(const GrPipelineInfo& init) override {
702         // Handle any color overrides
703         if (init.fColorIgnored) {
704             fGeoData[0].fColor = GrColor_ILLEGAL;
705         } else if (GrColor_ILLEGAL != init.fOverrideColor) {
706             fGeoData[0].fColor = init.fOverrideColor;
707         }
708 
709         // setup batch properties
710         fBatch.fColorIgnored = init.fColorIgnored;
711         fBatch.fColor = fGeoData[0].fColor;
712         fBatch.fUsesLocalCoords = init.fUsesLocalCoords;
713         fBatch.fCoverageIgnored = init.fCoverageIgnored;
714         fBatch.fCoverage = fGeoData[0].fCoverage;
715     }
716 
717     void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override;
718 
geoData()719     SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
720 
721 private:
722     typedef SkTArray<SkPoint, true> PtArray;
723     typedef SkTArray<int, true> IntArray;
724     typedef SkTArray<float, true> FloatArray;
725 
AAHairlineBatch(const Geometry & geometry)726     AAHairlineBatch(const Geometry& geometry) {
727         this->initClassID<AAHairlineBatch>();
728         fGeoData.push_back(geometry);
729 
730         // compute bounds
731         fBounds = geometry.fPath.getBounds();
732         geometry.fViewMatrix.mapRect(&fBounds);
733 
734         // This is b.c. hairlines are notionally infinitely thin so without expansion
735         // two overlapping lines could be reordered even though they hit the same pixels.
736         fBounds.outset(0.5f, 0.5f);
737     }
738 
onCombineIfPossible(GrBatch * t)739     bool onCombineIfPossible(GrBatch* t) override {
740         AAHairlineBatch* that = t->cast<AAHairlineBatch>();
741 
742         if (this->viewMatrix().hasPerspective() != that->viewMatrix().hasPerspective()) {
743             return false;
744         }
745 
746         // We go to identity if we don't have perspective
747         if (this->viewMatrix().hasPerspective() &&
748             !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
749             return false;
750         }
751 
752         // TODO we can actually batch hairlines if they are the same color in a kind of bulk method
753         // but we haven't implemented this yet
754         // TODO investigate going to vertex color and coverage?
755         if (this->coverage() != that->coverage()) {
756             return false;
757         }
758 
759         if (this->color() != that->color()) {
760             return false;
761         }
762 
763         SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
764         if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
765             return false;
766         }
767 
768         fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin());
769         this->joinBounds(that->bounds());
770         return true;
771     }
772 
color() const773     GrColor color() const { return fBatch.fColor; }
coverage() const774     uint8_t coverage() const { return fBatch.fCoverage; }
usesLocalCoords() const775     bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
viewMatrix() const776     const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
777 
778     struct BatchTracker {
779         GrColor fColor;
780         uint8_t fCoverage;
781         SkRect fDevBounds;
782         bool fUsesLocalCoords;
783         bool fColorIgnored;
784         bool fCoverageIgnored;
785     };
786 
787     BatchTracker fBatch;
788     SkSTArray<1, Geometry, true> fGeoData;
789 };
790 
generateGeometry(GrBatchTarget * batchTarget,const GrPipeline * pipeline)791 void AAHairlineBatch::generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) {
792     // Setup the viewmatrix and localmatrix for the GrGeometryProcessor.
793     SkMatrix invert;
794     if (!this->viewMatrix().invert(&invert)) {
795         return;
796     }
797 
798     // we will transform to identity space if the viewmatrix does not have perspective
799     bool hasPerspective = this->viewMatrix().hasPerspective();
800     const SkMatrix* geometryProcessorViewM = &SkMatrix::I();
801     const SkMatrix* geometryProcessorLocalM = &invert;
802     const SkMatrix* toDevice = NULL;
803     const SkMatrix* toSrc = NULL;
804     if (hasPerspective) {
805         geometryProcessorViewM = &this->viewMatrix();
806         geometryProcessorLocalM = &SkMatrix::I();
807         toDevice = &this->viewMatrix();
808         toSrc = &invert;
809     }
810 
811     // Setup geometry processors for worst case
812     uint32_t gpFlags = GrDefaultGeoProcFactory::kPosition_GPType |
813                        GrDefaultGeoProcFactory::kCoverage_GPType;
814 
815     SkAutoTUnref<const GrGeometryProcessor> lineGP(
816             GrDefaultGeoProcFactory::Create(gpFlags,
817                                             this->color(),
818                                             *geometryProcessorViewM,
819                                             *geometryProcessorLocalM,
820                                             this->coverage()));
821 
822     SkAutoTUnref<const GrGeometryProcessor> quadGP(
823             GrQuadEffect::Create(this->color(),
824                                  *geometryProcessorViewM,
825                                  kHairlineAA_GrProcessorEdgeType,
826                                  batchTarget->caps(),
827                                  *geometryProcessorLocalM,
828                                  this->coverage()));
829 
830     SkAutoTUnref<const GrGeometryProcessor> conicGP(
831             GrConicEffect::Create(this->color(),
832                                   *geometryProcessorViewM,
833                                   kHairlineAA_GrProcessorEdgeType,
834                                   batchTarget->caps(),
835                                   *geometryProcessorLocalM,
836                                   this->coverage()));
837 
838     // This is hand inlined for maximum performance.
839     PREALLOC_PTARRAY(128) lines;
840     PREALLOC_PTARRAY(128) quads;
841     PREALLOC_PTARRAY(128) conics;
842     IntArray qSubdivs;
843     FloatArray cWeights;
844     int quadCount = 0;
845 
846     int instanceCount = fGeoData.count();
847     for (int i = 0; i < instanceCount; i++) {
848         const Geometry& args = fGeoData[i];
849         quadCount += gather_lines_and_quads(args.fPath, args.fViewMatrix, args.fDevClipBounds,
850                                             &lines, &quads, &conics, &qSubdivs, &cWeights);
851     }
852 
853     int lineCount = lines.count() / 2;
854     int conicCount = conics.count() / 3;
855 
856     // do lines first
857     if (lineCount) {
858         SkAutoTUnref<const GrIndexBuffer> linesIndexBuffer(
859             ref_lines_index_buffer(batchTarget->resourceProvider()));
860         batchTarget->initDraw(lineGP, pipeline);
861 
862         // TODO remove this when batch is everywhere
863         GrPipelineInfo init;
864         init.fColorIgnored = fBatch.fColorIgnored;
865         init.fOverrideColor = GrColor_ILLEGAL;
866         init.fCoverageIgnored = fBatch.fCoverageIgnored;
867         init.fUsesLocalCoords = this->usesLocalCoords();
868         lineGP->initBatchTracker(batchTarget->currentBatchTracker(), init);
869 
870         const GrVertexBuffer* vertexBuffer;
871         int firstVertex;
872 
873         size_t vertexStride = lineGP->getVertexStride();
874         int vertexCount = kLineSegNumVertices * lineCount;
875         LineVertex* verts = reinterpret_cast<LineVertex*>(
876             batchTarget->makeVertSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex));
877 
878         if (!verts|| !linesIndexBuffer) {
879             SkDebugf("Could not allocate vertices\n");
880             return;
881         }
882 
883         SkASSERT(lineGP->getVertexStride() == sizeof(LineVertex));
884 
885         for (int i = 0; i < lineCount; ++i) {
886             add_line(&lines[2*i], toSrc, this->coverage(), &verts);
887         }
888 
889         {
890             GrVertices vertices;
891             vertices.initInstanced(kTriangles_GrPrimitiveType, vertexBuffer, linesIndexBuffer,
892                                    firstVertex, kLineSegNumVertices, kIdxsPerLineSeg, lineCount,
893                                    kLineSegsNumInIdxBuffer);
894             batchTarget->draw(vertices);
895         }
896     }
897 
898     if (quadCount || conicCount) {
899         const GrVertexBuffer* vertexBuffer;
900         int firstVertex;
901 
902         SkAutoTUnref<const GrIndexBuffer> quadsIndexBuffer(
903             ref_quads_index_buffer(batchTarget->resourceProvider()));
904 
905         size_t vertexStride = sizeof(BezierVertex);
906         int vertexCount = kQuadNumVertices * quadCount + kQuadNumVertices * conicCount;
907         void *vertices = batchTarget->makeVertSpace(vertexStride, vertexCount,
908                                                     &vertexBuffer, &firstVertex);
909 
910         if (!vertices || !quadsIndexBuffer) {
911             SkDebugf("Could not allocate vertices\n");
912             return;
913         }
914 
915         // Setup vertices
916         BezierVertex* verts = reinterpret_cast<BezierVertex*>(vertices);
917 
918         int unsubdivQuadCnt = quads.count() / 3;
919         for (int i = 0; i < unsubdivQuadCnt; ++i) {
920             SkASSERT(qSubdivs[i] >= 0);
921             add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts);
922         }
923 
924         // Start Conics
925         for (int i = 0; i < conicCount; ++i) {
926             add_conics(&conics[3*i], cWeights[i], toDevice, toSrc, &verts);
927         }
928 
929         if (quadCount > 0) {
930             batchTarget->initDraw(quadGP, pipeline);
931 
932             // TODO remove this when batch is everywhere
933             GrPipelineInfo init;
934             init.fColorIgnored = fBatch.fColorIgnored;
935             init.fOverrideColor = GrColor_ILLEGAL;
936             init.fCoverageIgnored = fBatch.fCoverageIgnored;
937             init.fUsesLocalCoords = this->usesLocalCoords();
938             quadGP->initBatchTracker(batchTarget->currentBatchTracker(), init);
939 
940             {
941                 GrVertices verts;
942                 verts.initInstanced(kTriangles_GrPrimitiveType, vertexBuffer, quadsIndexBuffer,
943                                     firstVertex, kQuadNumVertices, kIdxsPerQuad, quadCount,
944                                     kQuadsNumInIdxBuffer);
945                 batchTarget->draw(verts);
946                 firstVertex += quadCount * kQuadNumVertices;
947            }
948         }
949 
950         if (conicCount > 0) {
951             batchTarget->initDraw(conicGP, pipeline);
952 
953             // TODO remove this when batch is everywhere
954             GrPipelineInfo init;
955             init.fColorIgnored = fBatch.fColorIgnored;
956             init.fOverrideColor = GrColor_ILLEGAL;
957             init.fCoverageIgnored = fBatch.fCoverageIgnored;
958             init.fUsesLocalCoords = this->usesLocalCoords();
959             conicGP->initBatchTracker(batchTarget->currentBatchTracker(), init);
960 
961             {
962                 GrVertices verts;
963                 verts.initInstanced(kTriangles_GrPrimitiveType, vertexBuffer, quadsIndexBuffer,
964                                     firstVertex, kQuadNumVertices, kIdxsPerQuad, conicCount,
965                                     kQuadsNumInIdxBuffer);
966                 batchTarget->draw(verts);
967             }
968         }
969     }
970 }
971 
create_hairline_batch(GrColor color,const SkMatrix & viewMatrix,const SkPath & path,const GrStrokeInfo & stroke,const SkIRect & devClipBounds)972 static GrBatch* create_hairline_batch(GrColor color,
973                                       const SkMatrix& viewMatrix,
974                                       const SkPath& path,
975                                       const GrStrokeInfo& stroke,
976                                       const SkIRect& devClipBounds) {
977     SkScalar hairlineCoverage;
978     uint8_t newCoverage = 0xff;
979     if (GrPathRenderer::IsStrokeHairlineOrEquivalent(stroke, viewMatrix, &hairlineCoverage)) {
980         newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff);
981     }
982 
983     AAHairlineBatch::Geometry geometry;
984     geometry.fColor = color;
985     geometry.fCoverage = newCoverage;
986     geometry.fViewMatrix = viewMatrix;
987     geometry.fPath = path;
988     geometry.fDevClipBounds = devClipBounds;
989 
990     return AAHairlineBatch::Create(geometry);
991 }
992 
onDrawPath(GrDrawTarget * target,GrPipelineBuilder * pipelineBuilder,GrColor color,const SkMatrix & viewMatrix,const SkPath & path,const GrStrokeInfo & stroke,bool)993 bool GrAAHairLinePathRenderer::onDrawPath(GrDrawTarget* target,
994                                           GrPipelineBuilder* pipelineBuilder,
995                                           GrColor color,
996                                           const SkMatrix& viewMatrix,
997                                           const SkPath& path,
998                                           const GrStrokeInfo& stroke,
999                                           bool) {
1000     SkIRect devClipBounds;
1001     pipelineBuilder->clip().getConservativeBounds(pipelineBuilder->getRenderTarget(),
1002                                                   &devClipBounds);
1003 
1004     SkAutoTUnref<GrBatch> batch(create_hairline_batch(color, viewMatrix, path, stroke,
1005                                                       devClipBounds));
1006     target->drawBatch(pipelineBuilder, batch);
1007 
1008     return true;
1009 }
1010 
1011 ///////////////////////////////////////////////////////////////////////////////////////////////////
1012 
1013 #ifdef GR_TEST_UTILS
1014 
BATCH_TEST_DEFINE(AAHairlineBatch)1015 BATCH_TEST_DEFINE(AAHairlineBatch) {
1016     GrColor color = GrRandomColor(random);
1017     SkMatrix viewMatrix = GrTest::TestMatrix(random);
1018     GrStrokeInfo stroke(SkStrokeRec::kHairline_InitStyle);
1019     SkPath path = GrTest::TestPath(random);
1020     SkIRect devClipBounds;
1021     devClipBounds.setEmpty();
1022     return create_hairline_batch(color, viewMatrix, path, stroke, devClipBounds);
1023 }
1024 
1025 #endif
1026