Line data Source code
1 : /*
2 : * Copyright 2012 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 "GrAAConvexPathRenderer.h"
9 :
10 : #include "GrAAConvexTessellator.h"
11 : #include "GrCaps.h"
12 : #include "GrContext.h"
13 : #include "GrDefaultGeoProcFactory.h"
14 : #include "GrDrawOpTest.h"
15 : #include "GrGeometryProcessor.h"
16 : #include "GrOpFlushState.h"
17 : #include "GrPathUtils.h"
18 : #include "GrPipelineBuilder.h"
19 : #include "GrProcessor.h"
20 : #include "SkGeometry.h"
21 : #include "SkPathPriv.h"
22 : #include "SkString.h"
23 : #include "SkTraceEvent.h"
24 : #include "glsl/GrGLSLFragmentShaderBuilder.h"
25 : #include "glsl/GrGLSLGeometryProcessor.h"
26 : #include "glsl/GrGLSLProgramDataManager.h"
27 : #include "glsl/GrGLSLUniformHandler.h"
28 : #include "glsl/GrGLSLVarying.h"
29 : #include "glsl/GrGLSLVertexShaderBuilder.h"
30 : #include "ops/GrMeshDrawOp.h"
31 :
32 0 : GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
33 0 : }
34 :
35 : struct Segment {
36 : enum {
37 : // These enum values are assumed in member functions below.
38 : kLine = 0,
39 : kQuad = 1,
40 : } fType;
41 :
42 : // line uses one pt, quad uses 2 pts
43 : SkPoint fPts[2];
44 : // normal to edge ending at each pt
45 : SkVector fNorms[2];
46 : // is the corner where the previous segment meets this segment
47 : // sharp. If so, fMid is a normalized bisector facing outward.
48 : SkVector fMid;
49 :
50 0 : int countPoints() {
51 : GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
52 0 : return fType + 1;
53 : }
54 0 : const SkPoint& endPt() const {
55 : GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
56 0 : return fPts[fType];
57 : }
58 0 : const SkPoint& endNorm() const {
59 : GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
60 0 : return fNorms[fType];
61 : }
62 : };
63 :
64 : typedef SkTArray<Segment, true> SegmentArray;
65 :
66 0 : static void center_of_mass(const SegmentArray& segments, SkPoint* c) {
67 0 : SkScalar area = 0;
68 0 : SkPoint center = {0, 0};
69 0 : int count = segments.count();
70 0 : SkPoint p0 = {0, 0};
71 0 : if (count > 2) {
72 : // We translate the polygon so that the first point is at the origin.
73 : // This avoids some precision issues with small area polygons far away
74 : // from the origin.
75 0 : p0 = segments[0].endPt();
76 : SkPoint pi;
77 : SkPoint pj;
78 : // the first and last iteration of the below loop would compute
79 : // zeros since the starting / ending point is (0,0). So instead we start
80 : // at i=1 and make the last iteration i=count-2.
81 0 : pj = segments[1].endPt() - p0;
82 0 : for (int i = 1; i < count - 1; ++i) {
83 0 : pi = pj;
84 0 : pj = segments[i + 1].endPt() - p0;
85 :
86 0 : SkScalar t = SkPoint::CrossProduct(pi, pj);
87 0 : area += t;
88 0 : center.fX += (pi.fX + pj.fX) * t;
89 0 : center.fY += (pi.fY + pj.fY) * t;
90 : }
91 : }
92 :
93 : // If the poly has no area then we instead return the average of
94 : // its points.
95 0 : if (SkScalarNearlyZero(area)) {
96 : SkPoint avg;
97 0 : avg.set(0, 0);
98 0 : for (int i = 0; i < count; ++i) {
99 0 : const SkPoint& pt = segments[i].endPt();
100 0 : avg.fX += pt.fX;
101 0 : avg.fY += pt.fY;
102 : }
103 0 : SkScalar denom = SK_Scalar1 / count;
104 0 : avg.scale(denom);
105 0 : *c = avg;
106 : } else {
107 0 : area *= 3;
108 0 : area = SkScalarInvert(area);
109 0 : center.scale(area);
110 : // undo the translate of p0 to the origin.
111 0 : *c = center + p0;
112 : }
113 0 : SkASSERT(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY));
114 0 : }
115 :
116 0 : static void compute_vectors(SegmentArray* segments,
117 : SkPoint* fanPt,
118 : SkPathPriv::FirstDirection dir,
119 : int* vCount,
120 : int* iCount) {
121 0 : center_of_mass(*segments, fanPt);
122 0 : int count = segments->count();
123 :
124 : // Make the normals point towards the outside
125 : SkPoint::Side normSide;
126 0 : if (dir == SkPathPriv::kCCW_FirstDirection) {
127 0 : normSide = SkPoint::kRight_Side;
128 : } else {
129 0 : normSide = SkPoint::kLeft_Side;
130 : }
131 :
132 0 : *vCount = 0;
133 0 : *iCount = 0;
134 : // compute normals at all points
135 0 : for (int a = 0; a < count; ++a) {
136 0 : Segment& sega = (*segments)[a];
137 0 : int b = (a + 1) % count;
138 0 : Segment& segb = (*segments)[b];
139 :
140 0 : const SkPoint* prevPt = &sega.endPt();
141 0 : int n = segb.countPoints();
142 0 : for (int p = 0; p < n; ++p) {
143 0 : segb.fNorms[p] = segb.fPts[p] - *prevPt;
144 0 : segb.fNorms[p].normalize();
145 0 : segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
146 0 : prevPt = &segb.fPts[p];
147 : }
148 0 : if (Segment::kLine == segb.fType) {
149 0 : *vCount += 5;
150 0 : *iCount += 9;
151 : } else {
152 0 : *vCount += 6;
153 0 : *iCount += 12;
154 : }
155 : }
156 :
157 : // compute mid-vectors where segments meet. TODO: Detect shallow corners
158 : // and leave out the wedges and close gaps by stitching segments together.
159 0 : for (int a = 0; a < count; ++a) {
160 0 : const Segment& sega = (*segments)[a];
161 0 : int b = (a + 1) % count;
162 0 : Segment& segb = (*segments)[b];
163 0 : segb.fMid = segb.fNorms[0] + sega.endNorm();
164 0 : segb.fMid.normalize();
165 : // corner wedges
166 0 : *vCount += 4;
167 0 : *iCount += 6;
168 : }
169 0 : }
170 :
171 : struct DegenerateTestData {
172 0 : DegenerateTestData() { fStage = kInitial; }
173 0 : bool isDegenerate() const { return kNonDegenerate != fStage; }
174 : enum {
175 : kInitial,
176 : kPoint,
177 : kLine,
178 : kNonDegenerate
179 : } fStage;
180 : SkPoint fFirstPoint;
181 : SkVector fLineNormal;
182 : SkScalar fLineC;
183 : };
184 :
185 : static const SkScalar kClose = (SK_Scalar1 / 16);
186 : static const SkScalar kCloseSqd = kClose * kClose;
187 :
188 0 : static void update_degenerate_test(DegenerateTestData* data, const SkPoint& pt) {
189 0 : switch (data->fStage) {
190 : case DegenerateTestData::kInitial:
191 0 : data->fFirstPoint = pt;
192 0 : data->fStage = DegenerateTestData::kPoint;
193 0 : break;
194 : case DegenerateTestData::kPoint:
195 0 : if (pt.distanceToSqd(data->fFirstPoint) > kCloseSqd) {
196 0 : data->fLineNormal = pt - data->fFirstPoint;
197 0 : data->fLineNormal.normalize();
198 0 : data->fLineNormal.setOrthog(data->fLineNormal);
199 0 : data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
200 0 : data->fStage = DegenerateTestData::kLine;
201 : }
202 0 : break;
203 : case DegenerateTestData::kLine:
204 0 : if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
205 0 : data->fStage = DegenerateTestData::kNonDegenerate;
206 : }
207 : case DegenerateTestData::kNonDegenerate:
208 0 : break;
209 : default:
210 0 : SkFAIL("Unexpected degenerate test stage.");
211 : }
212 0 : }
213 :
214 0 : static inline bool get_direction(const SkPath& path, const SkMatrix& m,
215 : SkPathPriv::FirstDirection* dir) {
216 0 : if (!SkPathPriv::CheapComputeFirstDirection(path, dir)) {
217 0 : return false;
218 : }
219 : // check whether m reverses the orientation
220 0 : SkASSERT(!m.hasPerspective());
221 0 : SkScalar det2x2 = m.get(SkMatrix::kMScaleX) * m.get(SkMatrix::kMScaleY) -
222 0 : m.get(SkMatrix::kMSkewX) * m.get(SkMatrix::kMSkewY);
223 0 : if (det2x2 < 0) {
224 0 : *dir = SkPathPriv::OppositeFirstDirection(*dir);
225 : }
226 0 : return true;
227 : }
228 :
229 0 : static inline void add_line_to_segment(const SkPoint& pt,
230 : SegmentArray* segments) {
231 0 : segments->push_back();
232 0 : segments->back().fType = Segment::kLine;
233 0 : segments->back().fPts[0] = pt;
234 0 : }
235 :
236 0 : static inline void add_quad_segment(const SkPoint pts[3],
237 : SegmentArray* segments) {
238 0 : if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) {
239 0 : if (pts[0] != pts[2]) {
240 0 : add_line_to_segment(pts[2], segments);
241 : }
242 : } else {
243 0 : segments->push_back();
244 0 : segments->back().fType = Segment::kQuad;
245 0 : segments->back().fPts[0] = pts[1];
246 0 : segments->back().fPts[1] = pts[2];
247 : }
248 0 : }
249 :
250 0 : static inline void add_cubic_segments(const SkPoint pts[4],
251 : SkPathPriv::FirstDirection dir,
252 : SegmentArray* segments) {
253 0 : SkSTArray<15, SkPoint, true> quads;
254 0 : GrPathUtils::convertCubicToQuadsConstrainToTangents(pts, SK_Scalar1, dir, &quads);
255 0 : int count = quads.count();
256 0 : for (int q = 0; q < count; q += 3) {
257 0 : add_quad_segment(&quads[q], segments);
258 : }
259 0 : }
260 :
261 0 : static bool get_segments(const SkPath& path,
262 : const SkMatrix& m,
263 : SegmentArray* segments,
264 : SkPoint* fanPt,
265 : int* vCount,
266 : int* iCount) {
267 0 : SkPath::Iter iter(path, true);
268 : // This renderer over-emphasizes very thin path regions. We use the distance
269 : // to the path from the sample to compute coverage. Every pixel intersected
270 : // by the path will be hit and the maximum distance is sqrt(2)/2. We don't
271 : // notice that the sample may be close to a very thin area of the path and
272 : // thus should be very light. This is particularly egregious for degenerate
273 : // line paths. We detect paths that are very close to a line (zero area) and
274 : // draw nothing.
275 0 : DegenerateTestData degenerateData;
276 : SkPathPriv::FirstDirection dir;
277 : // get_direction can fail for some degenerate paths.
278 0 : if (!get_direction(path, m, &dir)) {
279 0 : return false;
280 : }
281 :
282 : for (;;) {
283 : SkPoint pts[4];
284 0 : SkPath::Verb verb = iter.next(pts, true, true);
285 0 : switch (verb) {
286 : case SkPath::kMove_Verb:
287 0 : m.mapPoints(pts, 1);
288 0 : update_degenerate_test(°enerateData, pts[0]);
289 0 : break;
290 : case SkPath::kLine_Verb: {
291 0 : m.mapPoints(&pts[1], 1);
292 0 : update_degenerate_test(°enerateData, pts[1]);
293 0 : add_line_to_segment(pts[1], segments);
294 0 : break;
295 : }
296 : case SkPath::kQuad_Verb:
297 0 : m.mapPoints(pts, 3);
298 0 : update_degenerate_test(°enerateData, pts[1]);
299 0 : update_degenerate_test(°enerateData, pts[2]);
300 0 : add_quad_segment(pts, segments);
301 0 : break;
302 : case SkPath::kConic_Verb: {
303 0 : m.mapPoints(pts, 3);
304 0 : SkScalar weight = iter.conicWeight();
305 0 : SkAutoConicToQuads converter;
306 0 : const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5f);
307 0 : for (int i = 0; i < converter.countQuads(); ++i) {
308 0 : update_degenerate_test(°enerateData, quadPts[2*i + 1]);
309 0 : update_degenerate_test(°enerateData, quadPts[2*i + 2]);
310 0 : add_quad_segment(quadPts + 2*i, segments);
311 : }
312 0 : break;
313 : }
314 : case SkPath::kCubic_Verb: {
315 0 : m.mapPoints(pts, 4);
316 0 : update_degenerate_test(°enerateData, pts[1]);
317 0 : update_degenerate_test(°enerateData, pts[2]);
318 0 : update_degenerate_test(°enerateData, pts[3]);
319 0 : add_cubic_segments(pts, dir, segments);
320 0 : break;
321 : };
322 : case SkPath::kDone_Verb:
323 0 : if (degenerateData.isDegenerate()) {
324 0 : return false;
325 : } else {
326 0 : compute_vectors(segments, fanPt, dir, vCount, iCount);
327 0 : return true;
328 : }
329 : default:
330 0 : break;
331 : }
332 0 : }
333 : }
334 :
335 : struct QuadVertex {
336 : SkPoint fPos;
337 : SkPoint fUV;
338 : SkScalar fD0;
339 : SkScalar fD1;
340 : };
341 :
342 : struct Draw {
343 0 : Draw() : fVertexCnt(0), fIndexCnt(0) {}
344 : int fVertexCnt;
345 : int fIndexCnt;
346 : };
347 :
348 : typedef SkTArray<Draw, true> DrawArray;
349 :
350 0 : static void create_vertices(const SegmentArray& segments,
351 : const SkPoint& fanPt,
352 : DrawArray* draws,
353 : QuadVertex* verts,
354 : uint16_t* idxs) {
355 0 : Draw* draw = &draws->push_back();
356 : // alias just to make vert/index assignments easier to read.
357 0 : int* v = &draw->fVertexCnt;
358 0 : int* i = &draw->fIndexCnt;
359 :
360 0 : int count = segments.count();
361 0 : for (int a = 0; a < count; ++a) {
362 0 : const Segment& sega = segments[a];
363 0 : int b = (a + 1) % count;
364 0 : const Segment& segb = segments[b];
365 :
366 : // Check whether adding the verts for this segment to the current draw would cause index
367 : // values to overflow.
368 0 : int vCount = 4;
369 0 : if (Segment::kLine == segb.fType) {
370 0 : vCount += 5;
371 : } else {
372 0 : vCount += 6;
373 : }
374 0 : if (draw->fVertexCnt + vCount > (1 << 16)) {
375 0 : verts += *v;
376 0 : idxs += *i;
377 0 : draw = &draws->push_back();
378 0 : v = &draw->fVertexCnt;
379 0 : i = &draw->fIndexCnt;
380 : }
381 :
382 : // FIXME: These tris are inset in the 1 unit arc around the corner
383 0 : verts[*v + 0].fPos = sega.endPt();
384 0 : verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm();
385 0 : verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid;
386 0 : verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0];
387 0 : verts[*v + 0].fUV.set(0,0);
388 0 : verts[*v + 1].fUV.set(0,-SK_Scalar1);
389 0 : verts[*v + 2].fUV.set(0,-SK_Scalar1);
390 0 : verts[*v + 3].fUV.set(0,-SK_Scalar1);
391 0 : verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
392 0 : verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
393 0 : verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
394 0 : verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
395 :
396 0 : idxs[*i + 0] = *v + 0;
397 0 : idxs[*i + 1] = *v + 2;
398 0 : idxs[*i + 2] = *v + 1;
399 0 : idxs[*i + 3] = *v + 0;
400 0 : idxs[*i + 4] = *v + 3;
401 0 : idxs[*i + 5] = *v + 2;
402 :
403 0 : *v += 4;
404 0 : *i += 6;
405 :
406 0 : if (Segment::kLine == segb.fType) {
407 0 : verts[*v + 0].fPos = fanPt;
408 0 : verts[*v + 1].fPos = sega.endPt();
409 0 : verts[*v + 2].fPos = segb.fPts[0];
410 :
411 0 : verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0];
412 0 : verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0];
413 :
414 : // we draw the line edge as a degenerate quad (u is 0, v is the
415 : // signed distance to the edge)
416 0 : SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos,
417 0 : verts[*v + 2].fPos);
418 0 : verts[*v + 0].fUV.set(0, dist);
419 0 : verts[*v + 1].fUV.set(0, 0);
420 0 : verts[*v + 2].fUV.set(0, 0);
421 0 : verts[*v + 3].fUV.set(0, -SK_Scalar1);
422 0 : verts[*v + 4].fUV.set(0, -SK_Scalar1);
423 :
424 0 : verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
425 0 : verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
426 0 : verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
427 0 : verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
428 0 : verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
429 :
430 0 : idxs[*i + 0] = *v + 3;
431 0 : idxs[*i + 1] = *v + 1;
432 0 : idxs[*i + 2] = *v + 2;
433 :
434 0 : idxs[*i + 3] = *v + 4;
435 0 : idxs[*i + 4] = *v + 3;
436 0 : idxs[*i + 5] = *v + 2;
437 :
438 0 : *i += 6;
439 :
440 : // Draw the interior fan if it exists.
441 : // TODO: Detect and combine colinear segments. This will ensure we catch every case
442 : // with no interior, and that the resulting shared edge uses the same endpoints.
443 0 : if (count >= 3) {
444 0 : idxs[*i + 0] = *v + 0;
445 0 : idxs[*i + 1] = *v + 2;
446 0 : idxs[*i + 2] = *v + 1;
447 :
448 0 : *i += 3;
449 : }
450 :
451 0 : *v += 5;
452 : } else {
453 0 : SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
454 :
455 0 : SkVector midVec = segb.fNorms[0] + segb.fNorms[1];
456 0 : midVec.normalize();
457 :
458 0 : verts[*v + 0].fPos = fanPt;
459 0 : verts[*v + 1].fPos = qpts[0];
460 0 : verts[*v + 2].fPos = qpts[2];
461 0 : verts[*v + 3].fPos = qpts[0] + segb.fNorms[0];
462 0 : verts[*v + 4].fPos = qpts[2] + segb.fNorms[1];
463 0 : verts[*v + 5].fPos = qpts[1] + midVec;
464 :
465 0 : SkScalar c = segb.fNorms[0].dot(qpts[0]);
466 0 : verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
467 0 : verts[*v + 1].fD0 = 0.f;
468 0 : verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
469 0 : verts[*v + 3].fD0 = -SK_ScalarMax/100;
470 0 : verts[*v + 4].fD0 = -SK_ScalarMax/100;
471 0 : verts[*v + 5].fD0 = -SK_ScalarMax/100;
472 :
473 0 : c = segb.fNorms[1].dot(qpts[2]);
474 0 : verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
475 0 : verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
476 0 : verts[*v + 2].fD1 = 0.f;
477 0 : verts[*v + 3].fD1 = -SK_ScalarMax/100;
478 0 : verts[*v + 4].fD1 = -SK_ScalarMax/100;
479 0 : verts[*v + 5].fD1 = -SK_ScalarMax/100;
480 :
481 0 : GrPathUtils::QuadUVMatrix toUV(qpts);
482 0 : toUV.apply<6, sizeof(QuadVertex), sizeof(SkPoint)>(verts + *v);
483 :
484 0 : idxs[*i + 0] = *v + 3;
485 0 : idxs[*i + 1] = *v + 1;
486 0 : idxs[*i + 2] = *v + 2;
487 0 : idxs[*i + 3] = *v + 4;
488 0 : idxs[*i + 4] = *v + 3;
489 0 : idxs[*i + 5] = *v + 2;
490 :
491 0 : idxs[*i + 6] = *v + 5;
492 0 : idxs[*i + 7] = *v + 3;
493 0 : idxs[*i + 8] = *v + 4;
494 :
495 0 : *i += 9;
496 :
497 : // Draw the interior fan if it exists.
498 : // TODO: Detect and combine colinear segments. This will ensure we catch every case
499 : // with no interior, and that the resulting shared edge uses the same endpoints.
500 0 : if (count >= 3) {
501 0 : idxs[*i + 0] = *v + 0;
502 0 : idxs[*i + 1] = *v + 2;
503 0 : idxs[*i + 2] = *v + 1;
504 :
505 0 : *i += 3;
506 : }
507 :
508 0 : *v += 6;
509 : }
510 : }
511 0 : }
512 :
513 : ///////////////////////////////////////////////////////////////////////////////
514 :
515 : /*
516 : * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
517 : * two components of the vertex attribute. Coverage is based on signed
518 : * distance with negative being inside, positive outside. The edge is specified in
519 : * window space (y-down). If either the third or fourth component of the interpolated
520 : * vertex coord is > 0 then the pixel is considered outside the edge. This is used to
521 : * attempt to trim to a portion of the infinite quad.
522 : * Requires shader derivative instruction support.
523 : */
524 :
525 : class QuadEdgeEffect : public GrGeometryProcessor {
526 : public:
527 :
528 0 : static sk_sp<GrGeometryProcessor> Make(GrColor color, const SkMatrix& localMatrix,
529 : bool usesLocalCoords) {
530 0 : return sk_sp<GrGeometryProcessor>(new QuadEdgeEffect(color, localMatrix, usesLocalCoords));
531 : }
532 :
533 0 : ~QuadEdgeEffect() override {}
534 :
535 0 : const char* name() const override { return "QuadEdge"; }
536 :
537 0 : const Attribute* inPosition() const { return fInPosition; }
538 0 : const Attribute* inQuadEdge() const { return fInQuadEdge; }
539 0 : GrColor color() const { return fColor; }
540 0 : const SkMatrix& localMatrix() const { return fLocalMatrix; }
541 0 : bool usesLocalCoords() const { return fUsesLocalCoords; }
542 :
543 0 : class GLSLProcessor : public GrGLSLGeometryProcessor {
544 : public:
545 0 : GLSLProcessor() : fColor(GrColor_ILLEGAL) {}
546 :
547 0 : void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
548 0 : const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
549 0 : GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
550 0 : GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
551 0 : GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
552 :
553 : // emit attributes
554 0 : varyingHandler->emitAttributes(qe);
555 :
556 0 : GrGLSLVertToFrag v(kVec4f_GrSLType);
557 0 : varyingHandler->addVarying("QuadEdge", &v);
558 0 : vertBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.inQuadEdge()->fName);
559 :
560 0 : GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
561 : // Setup pass through color
562 0 : this->setupUniformColor(fragBuilder, uniformHandler, args.fOutputColor,
563 0 : &fColorUniform);
564 :
565 : // Setup position
566 0 : this->setupPosition(vertBuilder, gpArgs, qe.inPosition()->fName);
567 :
568 : // emit transforms
569 0 : this->emitTransforms(vertBuilder,
570 : varyingHandler,
571 : uniformHandler,
572 : gpArgs->fPositionVar,
573 0 : qe.inPosition()->fName,
574 : qe.localMatrix(),
575 0 : args.fFPCoordTransformHandler);
576 :
577 0 : fragBuilder->codeAppendf("float edgeAlpha;");
578 :
579 : // keep the derivative instructions outside the conditional
580 0 : fragBuilder->codeAppendf("vec2 duvdx = dFdx(%s.xy);", v.fsIn());
581 0 : fragBuilder->codeAppendf("vec2 duvdy = dFdy(%s.xy);", v.fsIn());
582 0 : fragBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
583 : // today we know z and w are in device space. We could use derivatives
584 0 : fragBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(),
585 0 : v.fsIn());
586 0 : fragBuilder->codeAppendf ("} else {");
587 0 : fragBuilder->codeAppendf("vec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,"
588 : " 2.0*%s.x*duvdy.x - duvdy.y);",
589 0 : v.fsIn(), v.fsIn());
590 0 : fragBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
591 0 : v.fsIn());
592 0 : fragBuilder->codeAppendf("edgeAlpha = "
593 0 : "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);}");
594 :
595 0 : fragBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage);
596 0 : }
597 :
598 0 : static inline void GenKey(const GrGeometryProcessor& gp,
599 : const GrShaderCaps&,
600 : GrProcessorKeyBuilder* b) {
601 0 : const QuadEdgeEffect& qee = gp.cast<QuadEdgeEffect>();
602 0 : b->add32(SkToBool(qee.usesLocalCoords() && qee.localMatrix().hasPerspective()));
603 0 : }
604 :
605 0 : void setData(const GrGLSLProgramDataManager& pdman,
606 : const GrPrimitiveProcessor& gp,
607 : FPCoordTransformIter&& transformIter) override {
608 0 : const QuadEdgeEffect& qe = gp.cast<QuadEdgeEffect>();
609 0 : if (qe.color() != fColor) {
610 : float c[4];
611 0 : GrColorToRGBAFloat(qe.color(), c);
612 0 : pdman.set4fv(fColorUniform, 1, c);
613 0 : fColor = qe.color();
614 : }
615 0 : this->setTransformDataHelper(qe.fLocalMatrix, pdman, &transformIter);
616 0 : }
617 :
618 : private:
619 : GrColor fColor;
620 : UniformHandle fColorUniform;
621 :
622 : typedef GrGLSLGeometryProcessor INHERITED;
623 : };
624 :
625 0 : void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
626 0 : GLSLProcessor::GenKey(*this, caps, b);
627 0 : }
628 :
629 0 : GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
630 0 : return new GLSLProcessor();
631 : }
632 :
633 : private:
634 0 : QuadEdgeEffect(GrColor color, const SkMatrix& localMatrix, bool usesLocalCoords)
635 0 : : fColor(color)
636 : , fLocalMatrix(localMatrix)
637 0 : , fUsesLocalCoords(usesLocalCoords) {
638 0 : this->initClassID<QuadEdgeEffect>();
639 0 : fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType);
640 0 : fInQuadEdge = &this->addVertexAttrib("inQuadEdge", kVec4f_GrVertexAttribType);
641 0 : }
642 :
643 : const Attribute* fInPosition;
644 : const Attribute* fInQuadEdge;
645 : GrColor fColor;
646 : SkMatrix fLocalMatrix;
647 : bool fUsesLocalCoords;
648 :
649 : GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
650 :
651 : typedef GrGeometryProcessor INHERITED;
652 : };
653 :
654 : GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
655 :
656 : #if GR_TEST_UTILS
657 0 : sk_sp<GrGeometryProcessor> QuadEdgeEffect::TestCreate(GrProcessorTestData* d) {
658 : // Doesn't work without derivative instructions.
659 0 : return d->caps()->shaderCaps()->shaderDerivativeSupport()
660 : ? QuadEdgeEffect::Make(GrRandomColor(d->fRandom),
661 : GrTest::TestMatrix(d->fRandom),
662 0 : d->fRandom->nextBool())
663 0 : : nullptr;
664 : }
665 : #endif
666 :
667 : ///////////////////////////////////////////////////////////////////////////////
668 :
669 0 : bool GrAAConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
670 0 : return (args.fShaderCaps->shaderDerivativeSupport() && (GrAAType::kCoverage == args.fAAType) &&
671 0 : args.fShape->style().isSimpleFill() && !args.fShape->inverseFilled() &&
672 0 : args.fShape->knownToBeConvex());
673 : }
674 :
675 : // extract the result vertices and indices from the GrAAConvexTessellator
676 0 : static void extract_verts(const GrAAConvexTessellator& tess,
677 : void* vertices,
678 : size_t vertexStride,
679 : GrColor color,
680 : uint16_t* idxs,
681 : bool tweakAlphaForCoverage) {
682 0 : intptr_t verts = reinterpret_cast<intptr_t>(vertices);
683 :
684 0 : for (int i = 0; i < tess.numPts(); ++i) {
685 0 : *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
686 : }
687 :
688 : // Make 'verts' point to the colors
689 0 : verts += sizeof(SkPoint);
690 0 : for (int i = 0; i < tess.numPts(); ++i) {
691 0 : if (tweakAlphaForCoverage) {
692 0 : SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255);
693 0 : unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i));
694 0 : GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale);
695 0 : *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
696 : } else {
697 0 : *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
698 0 : *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) =
699 0 : tess.coverage(i);
700 : }
701 : }
702 :
703 0 : for (int i = 0; i < tess.numIndices(); ++i) {
704 0 : idxs[i] = tess.index(i);
705 : }
706 0 : }
707 :
708 0 : static sk_sp<GrGeometryProcessor> create_fill_gp(bool tweakAlphaForCoverage,
709 : const SkMatrix& viewMatrix,
710 : bool usesLocalCoords) {
711 : using namespace GrDefaultGeoProcFactory;
712 :
713 : Coverage::Type coverageType;
714 0 : if (tweakAlphaForCoverage) {
715 0 : coverageType = Coverage::kSolid_Type;
716 : } else {
717 0 : coverageType = Coverage::kAttribute_Type;
718 : }
719 : LocalCoords::Type localCoordsType =
720 0 : usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type;
721 : return MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, coverageType, localCoordsType,
722 0 : viewMatrix);
723 : }
724 :
725 0 : class AAConvexPathOp final : public GrLegacyMeshDrawOp {
726 : public:
727 0 : DEFINE_OP_CLASS_ID
728 0 : static std::unique_ptr<GrLegacyMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix,
729 : const SkPath& path) {
730 0 : return std::unique_ptr<GrLegacyMeshDrawOp>(new AAConvexPathOp(color, viewMatrix, path));
731 : }
732 :
733 0 : const char* name() const override { return "AAConvexPathOp"; }
734 :
735 0 : SkString dumpInfo() const override {
736 0 : SkString string;
737 0 : string.appendf("Color: 0x%08x, Count: %d\n", fColor, fPaths.count());
738 0 : string.append(DumpPipelineInfo(*this->pipeline()));
739 0 : string.append(INHERITED::dumpInfo());
740 0 : return string;
741 : }
742 :
743 : private:
744 0 : AAConvexPathOp(GrColor color, const SkMatrix& viewMatrix, const SkPath& path)
745 0 : : INHERITED(ClassID()), fColor(color) {
746 0 : fPaths.emplace_back(PathData{viewMatrix, path});
747 0 : this->setTransformedBounds(path.getBounds(), viewMatrix, HasAABloat::kYes, IsZeroArea::kNo);
748 0 : }
749 :
750 0 : void getProcessorAnalysisInputs(GrProcessorAnalysisColor* color,
751 : GrProcessorAnalysisCoverage* coverage) const override {
752 0 : color->setToConstant(fColor);
753 0 : *coverage = GrProcessorAnalysisCoverage::kSingleChannel;
754 0 : }
755 :
756 0 : void applyPipelineOptimizations(const PipelineOptimizations& optimizations) override {
757 0 : optimizations.getOverrideColorIfSet(&fColor);
758 :
759 0 : fUsesLocalCoords = optimizations.readsLocalCoords();
760 0 : fLinesOnly = SkPath::kLine_SegmentMask == fPaths[0].fPath.getSegmentMasks();
761 0 : fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage();
762 0 : }
763 :
764 0 : void prepareLinesOnlyDraws(Target* target) const {
765 0 : bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
766 :
767 : // Setup GrGeometryProcessor
768 : sk_sp<GrGeometryProcessor> gp(create_fill_gp(
769 0 : canTweakAlphaForCoverage, this->viewMatrix(), this->usesLocalCoords()));
770 0 : if (!gp) {
771 0 : SkDebugf("Could not create GrGeometryProcessor\n");
772 0 : return;
773 : }
774 :
775 0 : size_t vertexStride = gp->getVertexStride();
776 :
777 0 : SkASSERT(canTweakAlphaForCoverage ?
778 : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) :
779 : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr));
780 :
781 0 : GrAAConvexTessellator tess;
782 :
783 0 : int instanceCount = fPaths.count();
784 :
785 0 : for (int i = 0; i < instanceCount; i++) {
786 0 : tess.rewind();
787 :
788 0 : const PathData& args = fPaths[i];
789 :
790 0 : if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
791 0 : continue;
792 : }
793 :
794 : const GrBuffer* vertexBuffer;
795 : int firstVertex;
796 :
797 0 : void* verts = target->makeVertexSpace(vertexStride, tess.numPts(), &vertexBuffer,
798 0 : &firstVertex);
799 0 : if (!verts) {
800 0 : SkDebugf("Could not allocate vertices\n");
801 0 : return;
802 : }
803 :
804 : const GrBuffer* indexBuffer;
805 : int firstIndex;
806 :
807 0 : uint16_t* idxs = target->makeIndexSpace(tess.numIndices(), &indexBuffer, &firstIndex);
808 0 : if (!idxs) {
809 0 : SkDebugf("Could not allocate indices\n");
810 0 : return;
811 : }
812 :
813 0 : extract_verts(tess, verts, vertexStride, fColor, idxs, canTweakAlphaForCoverage);
814 :
815 0 : GrMesh mesh;
816 0 : mesh.initIndexed(kTriangles_GrPrimitiveType,
817 : vertexBuffer, indexBuffer,
818 : firstVertex, firstIndex,
819 0 : tess.numPts(), tess.numIndices());
820 0 : target->draw(gp.get(), this->pipeline(), mesh);
821 : }
822 : }
823 :
824 0 : void onPrepareDraws(Target* target) const override {
825 : #ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
826 0 : if (this->linesOnly()) {
827 0 : this->prepareLinesOnlyDraws(target);
828 0 : return;
829 : }
830 : #endif
831 :
832 0 : int instanceCount = fPaths.count();
833 :
834 : SkMatrix invert;
835 0 : if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
836 0 : SkDebugf("Could not invert viewmatrix\n");
837 0 : return;
838 : }
839 :
840 : // Setup GrGeometryProcessor
841 : sk_sp<GrGeometryProcessor> quadProcessor(
842 0 : QuadEdgeEffect::Make(this->color(), invert, this->usesLocalCoords()));
843 :
844 : // TODO generate all segments for all paths and use one vertex buffer
845 0 : for (int i = 0; i < instanceCount; i++) {
846 0 : const PathData& args = fPaths[i];
847 :
848 : // We use the fact that SkPath::transform path does subdivision based on
849 : // perspective. Otherwise, we apply the view matrix when copying to the
850 : // segment representation.
851 0 : const SkMatrix* viewMatrix = &args.fViewMatrix;
852 :
853 : // We avoid initializing the path unless we have to
854 0 : const SkPath* pathPtr = &args.fPath;
855 0 : SkTLazy<SkPath> tmpPath;
856 0 : if (viewMatrix->hasPerspective()) {
857 0 : SkPath* tmpPathPtr = tmpPath.init(*pathPtr);
858 0 : tmpPathPtr->setIsVolatile(true);
859 0 : tmpPathPtr->transform(*viewMatrix);
860 0 : viewMatrix = &SkMatrix::I();
861 0 : pathPtr = tmpPathPtr;
862 : }
863 :
864 : int vertexCount;
865 : int indexCount;
866 : enum {
867 : kPreallocSegmentCnt = 512 / sizeof(Segment),
868 : kPreallocDrawCnt = 4,
869 : };
870 0 : SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
871 : SkPoint fanPt;
872 :
873 0 : if (!get_segments(*pathPtr, *viewMatrix, &segments, &fanPt, &vertexCount,
874 : &indexCount)) {
875 0 : continue;
876 : }
877 :
878 : const GrBuffer* vertexBuffer;
879 : int firstVertex;
880 :
881 0 : size_t vertexStride = quadProcessor->getVertexStride();
882 0 : QuadVertex* verts = reinterpret_cast<QuadVertex*>(target->makeVertexSpace(
883 0 : vertexStride, vertexCount, &vertexBuffer, &firstVertex));
884 :
885 0 : if (!verts) {
886 0 : SkDebugf("Could not allocate vertices\n");
887 0 : return;
888 : }
889 :
890 : const GrBuffer* indexBuffer;
891 : int firstIndex;
892 :
893 0 : uint16_t *idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex);
894 0 : if (!idxs) {
895 0 : SkDebugf("Could not allocate indices\n");
896 0 : return;
897 : }
898 :
899 0 : SkSTArray<kPreallocDrawCnt, Draw, true> draws;
900 0 : create_vertices(segments, fanPt, &draws, verts, idxs);
901 :
902 0 : GrMesh mesh;
903 :
904 0 : for (int j = 0; j < draws.count(); ++j) {
905 0 : const Draw& draw = draws[j];
906 : mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer,
907 0 : firstVertex, firstIndex, draw.fVertexCnt, draw.fIndexCnt);
908 0 : target->draw(quadProcessor.get(), this->pipeline(), mesh);
909 0 : firstVertex += draw.fVertexCnt;
910 0 : firstIndex += draw.fIndexCnt;
911 : }
912 : }
913 : }
914 :
915 0 : bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
916 0 : AAConvexPathOp* that = t->cast<AAConvexPathOp>();
917 0 : if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
918 : that->bounds(), caps)) {
919 0 : return false;
920 : }
921 :
922 0 : if (this->color() != that->color()) {
923 0 : return false;
924 : }
925 :
926 0 : SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
927 0 : if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
928 0 : return false;
929 : }
930 :
931 0 : if (this->linesOnly() != that->linesOnly()) {
932 0 : return false;
933 : }
934 :
935 : // In the event of two ops, one who can tweak, one who cannot, we just fall back to not
936 : // tweaking
937 0 : if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) {
938 0 : fCanTweakAlphaForCoverage = false;
939 : }
940 :
941 0 : fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin());
942 0 : this->joinBounds(*that);
943 0 : return true;
944 : }
945 :
946 0 : GrColor color() const { return fColor; }
947 0 : bool linesOnly() const { return fLinesOnly; }
948 0 : bool usesLocalCoords() const { return fUsesLocalCoords; }
949 0 : bool canTweakAlphaForCoverage() const { return fCanTweakAlphaForCoverage; }
950 0 : const SkMatrix& viewMatrix() const { return fPaths[0].fViewMatrix; }
951 :
952 : GrColor fColor;
953 : bool fUsesLocalCoords;
954 : bool fLinesOnly;
955 : bool fCanTweakAlphaForCoverage;
956 :
957 0 : struct PathData {
958 : SkMatrix fViewMatrix;
959 : SkPath fPath;
960 : };
961 :
962 : SkSTArray<1, PathData, true> fPaths;
963 :
964 : typedef GrLegacyMeshDrawOp INHERITED;
965 : };
966 :
967 0 : bool GrAAConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
968 0 : GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(),
969 : "GrAAConvexPathRenderer::onDrawPath");
970 0 : SkASSERT(!args.fRenderTargetContext->isUnifiedMultisampled());
971 0 : SkASSERT(!args.fShape->isEmpty());
972 :
973 0 : SkPath path;
974 0 : args.fShape->asPath(&path);
975 :
976 : std::unique_ptr<GrLegacyMeshDrawOp> op =
977 0 : AAConvexPathOp::Make(args.fPaint.getColor(), *args.fViewMatrix, path);
978 :
979 0 : GrPipelineBuilder pipelineBuilder(std::move(args.fPaint), args.fAAType);
980 0 : pipelineBuilder.setUserStencil(args.fUserStencilSettings);
981 :
982 0 : args.fRenderTargetContext->addLegacyMeshDrawOp(std::move(pipelineBuilder), *args.fClip,
983 0 : std::move(op));
984 :
985 0 : return true;
986 :
987 : }
988 :
989 : ///////////////////////////////////////////////////////////////////////////////////////////////////
990 :
991 : #if GR_TEST_UTILS
992 :
993 0 : DRAW_OP_TEST_DEFINE(AAConvexPathOp) {
994 0 : GrColor color = GrRandomColor(random);
995 0 : SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
996 0 : SkPath path = GrTest::TestPathConvex(random);
997 :
998 0 : return AAConvexPathOp::Make(color, viewMatrix, path);
999 : }
1000 :
1001 : #endif
|