Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(309)

Side by Side Diff: src/gpu/GrAAConvexPathRenderer.cpp

Issue 1306143005: Move Pathrenderers to batches folder (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: rebase Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/gpu/GrAAConvexPathRenderer.h ('k') | src/gpu/GrAAConvexTessellator.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1
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 "GrAAConvexTessellator.h"
12 #include "GrBatchFlushState.h"
13 #include "GrBatchTest.h"
14 #include "GrCaps.h"
15 #include "GrContext.h"
16 #include "GrDefaultGeoProcFactory.h"
17 #include "GrGeometryProcessor.h"
18 #include "GrInvariantOutput.h"
19 #include "GrPathUtils.h"
20 #include "GrProcessor.h"
21 #include "GrPipelineBuilder.h"
22 #include "GrStrokeInfo.h"
23 #include "SkGeometry.h"
24 #include "SkPathPriv.h"
25 #include "SkString.h"
26 #include "SkTraceEvent.h"
27 #include "batches/GrVertexBatch.h"
28 #include "gl/GrGLProcessor.h"
29 #include "gl/GrGLGeometryProcessor.h"
30 #include "gl/builders/GrGLProgramBuilder.h"
31
32 GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
33 }
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 int countPoints() {
51 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
52 return fType + 1;
53 }
54 const SkPoint& endPt() const {
55 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
56 return fPts[fType];
57 };
58 const SkPoint& endNorm() const {
59 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
60 return fNorms[fType];
61 };
62 };
63
64 typedef SkTArray<Segment, true> SegmentArray;
65
66 static void center_of_mass(const SegmentArray& segments, SkPoint* c) {
67 SkScalar area = 0;
68 SkPoint center = {0, 0};
69 int count = segments.count();
70 SkPoint p0 = {0, 0};
71 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 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 pj = segments[1].endPt() - p0;
82 for (int i = 1; i < count - 1; ++i) {
83 pi = pj;
84 const SkPoint pj = segments[i + 1].endPt() - p0;
85
86 SkScalar t = SkScalarMul(pi.fX, pj.fY) - SkScalarMul(pj.fX, pi.fY);
87 area += t;
88 center.fX += (pi.fX + pj.fX) * t;
89 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 if (SkScalarNearlyZero(area)) {
96 SkPoint avg;
97 avg.set(0, 0);
98 for (int i = 0; i < count; ++i) {
99 const SkPoint& pt = segments[i].endPt();
100 avg.fX += pt.fX;
101 avg.fY += pt.fY;
102 }
103 SkScalar denom = SK_Scalar1 / count;
104 avg.scale(denom);
105 *c = avg;
106 } else {
107 area *= 3;
108 area = SkScalarInvert(area);
109 center.fX = SkScalarMul(center.fX, area);
110 center.fY = SkScalarMul(center.fY, area);
111 // undo the translate of p0 to the origin.
112 *c = center + p0;
113 }
114 SkASSERT(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY));
115 }
116
117 static void compute_vectors(SegmentArray* segments,
118 SkPoint* fanPt,
119 SkPathPriv::FirstDirection dir,
120 int* vCount,
121 int* iCount) {
122 center_of_mass(*segments, fanPt);
123 int count = segments->count();
124
125 // Make the normals point towards the outside
126 SkPoint::Side normSide;
127 if (dir == SkPathPriv::kCCW_FirstDirection) {
128 normSide = SkPoint::kRight_Side;
129 } else {
130 normSide = SkPoint::kLeft_Side;
131 }
132
133 *vCount = 0;
134 *iCount = 0;
135 // compute normals at all points
136 for (int a = 0; a < count; ++a) {
137 Segment& sega = (*segments)[a];
138 int b = (a + 1) % count;
139 Segment& segb = (*segments)[b];
140
141 const SkPoint* prevPt = &sega.endPt();
142 int n = segb.countPoints();
143 for (int p = 0; p < n; ++p) {
144 segb.fNorms[p] = segb.fPts[p] - *prevPt;
145 segb.fNorms[p].normalize();
146 segb.fNorms[p].setOrthog(segb.fNorms[p], normSide);
147 prevPt = &segb.fPts[p];
148 }
149 if (Segment::kLine == segb.fType) {
150 *vCount += 5;
151 *iCount += 9;
152 } else {
153 *vCount += 6;
154 *iCount += 12;
155 }
156 }
157
158 // compute mid-vectors where segments meet. TODO: Detect shallow corners
159 // and leave out the wedges and close gaps by stitching segments together.
160 for (int a = 0; a < count; ++a) {
161 const Segment& sega = (*segments)[a];
162 int b = (a + 1) % count;
163 Segment& segb = (*segments)[b];
164 segb.fMid = segb.fNorms[0] + sega.endNorm();
165 segb.fMid.normalize();
166 // corner wedges
167 *vCount += 4;
168 *iCount += 6;
169 }
170 }
171
172 struct DegenerateTestData {
173 DegenerateTestData() { fStage = kInitial; }
174 bool isDegenerate() const { return kNonDegenerate != fStage; }
175 enum {
176 kInitial,
177 kPoint,
178 kLine,
179 kNonDegenerate
180 } fStage;
181 SkPoint fFirstPoint;
182 SkVector fLineNormal;
183 SkScalar fLineC;
184 };
185
186 static const SkScalar kClose = (SK_Scalar1 / 16);
187 static const SkScalar kCloseSqd = SkScalarMul(kClose, kClose);
188
189 static void update_degenerate_test(DegenerateTestData* data, const SkPoint& pt) {
190 switch (data->fStage) {
191 case DegenerateTestData::kInitial:
192 data->fFirstPoint = pt;
193 data->fStage = DegenerateTestData::kPoint;
194 break;
195 case DegenerateTestData::kPoint:
196 if (pt.distanceToSqd(data->fFirstPoint) > kCloseSqd) {
197 data->fLineNormal = pt - data->fFirstPoint;
198 data->fLineNormal.normalize();
199 data->fLineNormal.setOrthog(data->fLineNormal);
200 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint);
201 data->fStage = DegenerateTestData::kLine;
202 }
203 break;
204 case DegenerateTestData::kLine:
205 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) {
206 data->fStage = DegenerateTestData::kNonDegenerate;
207 }
208 case DegenerateTestData::kNonDegenerate:
209 break;
210 default:
211 SkFAIL("Unexpected degenerate test stage.");
212 }
213 }
214
215 static inline bool get_direction(const SkPath& path, const SkMatrix& m,
216 SkPathPriv::FirstDirection* dir) {
217 if (!SkPathPriv::CheapComputeFirstDirection(path, dir)) {
218 return false;
219 }
220 // check whether m reverses the orientation
221 SkASSERT(!m.hasPerspective());
222 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMS caleY)) -
223 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSk ewY));
224 if (det2x2 < 0) {
225 *dir = SkPathPriv::OppositeFirstDirection(*dir);
226 }
227 return true;
228 }
229
230 static inline void add_line_to_segment(const SkPoint& pt,
231 SegmentArray* segments) {
232 segments->push_back();
233 segments->back().fType = Segment::kLine;
234 segments->back().fPts[0] = pt;
235 }
236
237 static inline void add_quad_segment(const SkPoint pts[3],
238 SegmentArray* segments) {
239 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) {
240 if (pts[0] != pts[2]) {
241 add_line_to_segment(pts[2], segments);
242 }
243 } else {
244 segments->push_back();
245 segments->back().fType = Segment::kQuad;
246 segments->back().fPts[0] = pts[1];
247 segments->back().fPts[1] = pts[2];
248 }
249 }
250
251 static inline void add_cubic_segments(const SkPoint pts[4],
252 SkPathPriv::FirstDirection dir,
253 SegmentArray* segments) {
254 SkSTArray<15, SkPoint, true> quads;
255 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads);
256 int count = quads.count();
257 for (int q = 0; q < count; q += 3) {
258 add_quad_segment(&quads[q], segments);
259 }
260 }
261
262 static bool get_segments(const SkPath& path,
263 const SkMatrix& m,
264 SegmentArray* segments,
265 SkPoint* fanPt,
266 int* vCount,
267 int* iCount) {
268 SkPath::Iter iter(path, true);
269 // This renderer over-emphasizes very thin path regions. We use the distance
270 // to the path from the sample to compute coverage. Every pixel intersected
271 // by the path will be hit and the maximum distance is sqrt(2)/2. We don't
272 // notice that the sample may be close to a very thin area of the path and
273 // thus should be very light. This is particularly egregious for degenerate
274 // line paths. We detect paths that are very close to a line (zero area) and
275 // draw nothing.
276 DegenerateTestData degenerateData;
277 SkPathPriv::FirstDirection dir;
278 // get_direction can fail for some degenerate paths.
279 if (!get_direction(path, m, &dir)) {
280 return false;
281 }
282
283 for (;;) {
284 SkPoint pts[4];
285 SkPath::Verb verb = iter.next(pts);
286 switch (verb) {
287 case SkPath::kMove_Verb:
288 m.mapPoints(pts, 1);
289 update_degenerate_test(&degenerateData, pts[0]);
290 break;
291 case SkPath::kLine_Verb: {
292 m.mapPoints(&pts[1], 1);
293 update_degenerate_test(&degenerateData, pts[1]);
294 add_line_to_segment(pts[1], segments);
295 break;
296 }
297 case SkPath::kQuad_Verb:
298 m.mapPoints(pts, 3);
299 update_degenerate_test(&degenerateData, pts[1]);
300 update_degenerate_test(&degenerateData, pts[2]);
301 add_quad_segment(pts, segments);
302 break;
303 case SkPath::kConic_Verb: {
304 m.mapPoints(pts, 3);
305 SkScalar weight = iter.conicWeight();
306 SkAutoConicToQuads converter;
307 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5 f);
308 for (int i = 0; i < converter.countQuads(); ++i) {
309 update_degenerate_test(&degenerateData, quadPts[2*i + 1]);
310 update_degenerate_test(&degenerateData, quadPts[2*i + 2]);
311 add_quad_segment(quadPts + 2*i, segments);
312 }
313 break;
314 }
315 case SkPath::kCubic_Verb: {
316 m.mapPoints(pts, 4);
317 update_degenerate_test(&degenerateData, pts[1]);
318 update_degenerate_test(&degenerateData, pts[2]);
319 update_degenerate_test(&degenerateData, pts[3]);
320 add_cubic_segments(pts, dir, segments);
321 break;
322 };
323 case SkPath::kDone_Verb:
324 if (degenerateData.isDegenerate()) {
325 return false;
326 } else {
327 compute_vectors(segments, fanPt, dir, vCount, iCount);
328 return true;
329 }
330 default:
331 break;
332 }
333 }
334 }
335
336 struct QuadVertex {
337 SkPoint fPos;
338 SkPoint fUV;
339 SkScalar fD0;
340 SkScalar fD1;
341 };
342
343 struct Draw {
344 Draw() : fVertexCnt(0), fIndexCnt(0) {}
345 int fVertexCnt;
346 int fIndexCnt;
347 };
348
349 typedef SkTArray<Draw, true> DrawArray;
350
351 static void create_vertices(const SegmentArray& segments,
352 const SkPoint& fanPt,
353 DrawArray* draws,
354 QuadVertex* verts,
355 uint16_t* idxs) {
356 Draw* draw = &draws->push_back();
357 // alias just to make vert/index assignments easier to read.
358 int* v = &draw->fVertexCnt;
359 int* i = &draw->fIndexCnt;
360
361 int count = segments.count();
362 for (int a = 0; a < count; ++a) {
363 const Segment& sega = segments[a];
364 int b = (a + 1) % count;
365 const Segment& segb = segments[b];
366
367 // Check whether adding the verts for this segment to the current draw w ould cause index
368 // values to overflow.
369 int vCount = 4;
370 if (Segment::kLine == segb.fType) {
371 vCount += 5;
372 } else {
373 vCount += 6;
374 }
375 if (draw->fVertexCnt + vCount > (1 << 16)) {
376 verts += *v;
377 idxs += *i;
378 draw = &draws->push_back();
379 v = &draw->fVertexCnt;
380 i = &draw->fIndexCnt;
381 }
382
383 // FIXME: These tris are inset in the 1 unit arc around the corner
384 verts[*v + 0].fPos = sega.endPt();
385 verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm();
386 verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid;
387 verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0];
388 verts[*v + 0].fUV.set(0,0);
389 verts[*v + 1].fUV.set(0,-SK_Scalar1);
390 verts[*v + 2].fUV.set(0,-SK_Scalar1);
391 verts[*v + 3].fUV.set(0,-SK_Scalar1);
392 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
393 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
394 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
395 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
396
397 idxs[*i + 0] = *v + 0;
398 idxs[*i + 1] = *v + 2;
399 idxs[*i + 2] = *v + 1;
400 idxs[*i + 3] = *v + 0;
401 idxs[*i + 4] = *v + 3;
402 idxs[*i + 5] = *v + 2;
403
404 *v += 4;
405 *i += 6;
406
407 if (Segment::kLine == segb.fType) {
408 verts[*v + 0].fPos = fanPt;
409 verts[*v + 1].fPos = sega.endPt();
410 verts[*v + 2].fPos = segb.fPts[0];
411
412 verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0];
413 verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0];
414
415 // we draw the line edge as a degenerate quad (u is 0, v is the
416 // signed distance to the edge)
417 SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos,
418 verts[*v + 2].fPos);
419 verts[*v + 0].fUV.set(0, dist);
420 verts[*v + 1].fUV.set(0, 0);
421 verts[*v + 2].fUV.set(0, 0);
422 verts[*v + 3].fUV.set(0, -SK_Scalar1);
423 verts[*v + 4].fUV.set(0, -SK_Scalar1);
424
425 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1;
426 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1;
427 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1;
428 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1;
429 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1;
430
431 idxs[*i + 0] = *v + 3;
432 idxs[*i + 1] = *v + 1;
433 idxs[*i + 2] = *v + 2;
434
435 idxs[*i + 3] = *v + 4;
436 idxs[*i + 4] = *v + 3;
437 idxs[*i + 5] = *v + 2;
438
439 *i += 6;
440
441 // Draw the interior fan if it exists.
442 // TODO: Detect and combine colinear segments. This will ensure we c atch every case
443 // with no interior, and that the resulting shared edge uses the sam e endpoints.
444 if (count >= 3) {
445 idxs[*i + 0] = *v + 0;
446 idxs[*i + 1] = *v + 2;
447 idxs[*i + 2] = *v + 1;
448
449 *i += 3;
450 }
451
452 *v += 5;
453 } else {
454 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]};
455
456 SkVector midVec = segb.fNorms[0] + segb.fNorms[1];
457 midVec.normalize();
458
459 verts[*v + 0].fPos = fanPt;
460 verts[*v + 1].fPos = qpts[0];
461 verts[*v + 2].fPos = qpts[2];
462 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0];
463 verts[*v + 4].fPos = qpts[2] + segb.fNorms[1];
464 verts[*v + 5].fPos = qpts[1] + midVec;
465
466 SkScalar c = segb.fNorms[0].dot(qpts[0]);
467 verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c;
468 verts[*v + 1].fD0 = 0.f;
469 verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c;
470 verts[*v + 3].fD0 = -SK_ScalarMax/100;
471 verts[*v + 4].fD0 = -SK_ScalarMax/100;
472 verts[*v + 5].fD0 = -SK_ScalarMax/100;
473
474 c = segb.fNorms[1].dot(qpts[2]);
475 verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c;
476 verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c;
477 verts[*v + 2].fD1 = 0.f;
478 verts[*v + 3].fD1 = -SK_ScalarMax/100;
479 verts[*v + 4].fD1 = -SK_ScalarMax/100;
480 verts[*v + 5].fD1 = -SK_ScalarMax/100;
481
482 GrPathUtils::QuadUVMatrix toUV(qpts);
483 toUV.apply<6, sizeof(QuadVertex), sizeof(SkPoint)>(verts + *v);
484
485 idxs[*i + 0] = *v + 3;
486 idxs[*i + 1] = *v + 1;
487 idxs[*i + 2] = *v + 2;
488 idxs[*i + 3] = *v + 4;
489 idxs[*i + 4] = *v + 3;
490 idxs[*i + 5] = *v + 2;
491
492 idxs[*i + 6] = *v + 5;
493 idxs[*i + 7] = *v + 3;
494 idxs[*i + 8] = *v + 4;
495
496 *i += 9;
497
498 // Draw the interior fan if it exists.
499 // TODO: Detect and combine colinear segments. This will ensure we c atch every case
500 // with no interior, and that the resulting shared edge uses the sam e endpoints.
501 if (count >= 3) {
502 idxs[*i + 0] = *v + 0;
503 idxs[*i + 1] = *v + 2;
504 idxs[*i + 2] = *v + 1;
505
506 *i += 3;
507 }
508
509 *v += 6;
510 }
511 }
512 }
513
514 ///////////////////////////////////////////////////////////////////////////////
515
516 /*
517 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first
518 * two components of the vertex attribute. Coverage is based on signed
519 * distance with negative being inside, positive outside. The edge is specified in
520 * window space (y-down). If either the third or fourth component of the interpo lated
521 * vertex coord is > 0 then the pixel is considered outside the edge. This is us ed to
522 * attempt to trim to a portion of the infinite quad.
523 * Requires shader derivative instruction support.
524 */
525
526 class QuadEdgeEffect : public GrGeometryProcessor {
527 public:
528
529 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& localMatri x,
530 bool usesLocalCoords) {
531 return new QuadEdgeEffect(color, localMatrix, usesLocalCoords);
532 }
533
534 virtual ~QuadEdgeEffect() {}
535
536 const char* name() const override { return "QuadEdge"; }
537
538 const Attribute* inPosition() const { return fInPosition; }
539 const Attribute* inQuadEdge() const { return fInQuadEdge; }
540 GrColor color() const { return fColor; }
541 bool colorIgnored() const { return GrColor_ILLEGAL == fColor; }
542 const SkMatrix& localMatrix() const { return fLocalMatrix; }
543 bool usesLocalCoords() const { return fUsesLocalCoords; }
544
545 class GLProcessor : public GrGLGeometryProcessor {
546 public:
547 GLProcessor(const GrGeometryProcessor&,
548 const GrBatchTracker&)
549 : fColor(GrColor_ILLEGAL) {}
550
551 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
552 const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>();
553 GrGLGPBuilder* pb = args.fPB;
554 GrGLVertexBuilder* vsBuilder = pb->getVertexShaderBuilder();
555
556 // emit attributes
557 vsBuilder->emitAttributes(qe);
558
559 GrGLVertToFrag v(kVec4f_GrSLType);
560 args.fPB->addVarying("QuadEdge", &v);
561 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.inQuadEdge()->fName );
562
563 // Setup pass through color
564 if (!qe.colorIgnored()) {
565 this->setupUniformColor(pb, args.fOutputColor, &fColorUniform);
566 }
567
568 // Setup position
569 this->setupPosition(pb, gpArgs, qe.inPosition()->fName);
570
571 // emit transforms
572 this->emitTransforms(args.fPB, gpArgs->fPositionVar, qe.inPosition() ->fName,
573 qe.localMatrix(), args.fTransformsIn, args.fTra nsformsOut);
574
575 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder( );
576
577 SkAssertResult(fsBuilder->enableFeature(
578 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature) );
579 fsBuilder->codeAppendf("float edgeAlpha;");
580
581 // keep the derivative instructions outside the conditional
582 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s.xy);", v.fsIn());
583 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s.xy);", v.fsIn());
584 fsBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn());
585 // today we know z and w are in device space. We could use derivativ es
586 fsBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0); ", v.fsIn(),
587 v.fsIn());
588 fsBuilder->codeAppendf ("} else {");
589 fsBuilder->codeAppendf("vec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,"
590 " 2.0*%s.x*duvdy.x - duvdy.y);" ,
591 v.fsIn(), v.fsIn());
592 fsBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(),
593 v.fsIn());
594 fsBuilder->codeAppendf("edgeAlpha = "
595 "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0 );}");
596
597 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage );
598 }
599
600 static inline void GenKey(const GrGeometryProcessor& gp,
601 const GrBatchTracker& bt,
602 const GrGLSLCaps&,
603 GrProcessorKeyBuilder* b) {
604 const QuadEdgeEffect& qee = gp.cast<QuadEdgeEffect>();
605 uint32_t key = 0;
606 key |= qee.usesLocalCoords() && qee.localMatrix().hasPerspective() ? 0x1 : 0x0;
607 key |= qee.colorIgnored() ? 0x2 : 0x0;
608 b->add32(key);
609 }
610
611 virtual void setData(const GrGLProgramDataManager& pdman,
612 const GrPrimitiveProcessor& gp,
613 const GrBatchTracker& bt) override {
614 const QuadEdgeEffect& qe = gp.cast<QuadEdgeEffect>();
615 if (qe.color() != fColor) {
616 GrGLfloat c[4];
617 GrColorToRGBAFloat(qe.color(), c);
618 pdman.set4fv(fColorUniform, 1, c);
619 fColor = qe.color();
620 }
621 }
622
623 void setTransformData(const GrPrimitiveProcessor& primProc,
624 const GrGLProgramDataManager& pdman,
625 int index,
626 const SkTArray<const GrCoordTransform*, true>& tra nsforms) override {
627 this->setTransformDataHelper<QuadEdgeEffect>(primProc, pdman, index, transforms);
628 }
629
630 private:
631 GrColor fColor;
632 UniformHandle fColorUniform;
633
634 typedef GrGLGeometryProcessor INHERITED;
635 };
636
637 virtual void getGLProcessorKey(const GrBatchTracker& bt,
638 const GrGLSLCaps& caps,
639 GrProcessorKeyBuilder* b) const override {
640 GLProcessor::GenKey(*this, bt, caps, b);
641 }
642
643 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt,
644 const GrGLSLCaps&) const ov erride {
645 return new GLProcessor(*this, bt);
646 }
647
648 private:
649 QuadEdgeEffect(GrColor color, const SkMatrix& localMatrix, bool usesLocalCoo rds)
650 : fColor(color)
651 , fLocalMatrix(localMatrix)
652 , fUsesLocalCoords(usesLocalCoords) {
653 this->initClassID<QuadEdgeEffect>();
654 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVe rtexAttribType));
655 fInQuadEdge = &this->addVertexAttrib(Attribute("inQuadEdge", kVec4f_GrVe rtexAttribType));
656 }
657
658 const Attribute* fInPosition;
659 const Attribute* fInQuadEdge;
660 GrColor fColor;
661 SkMatrix fLocalMatrix;
662 bool fUsesLocalCoords;
663
664 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
665
666 typedef GrGeometryProcessor INHERITED;
667 };
668
669 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect);
670
671 const GrGeometryProcessor* QuadEdgeEffect::TestCreate(GrProcessorTestData* d) {
672 // Doesn't work without derivative instructions.
673 return d->fCaps->shaderCaps()->shaderDerivativeSupport() ?
674 QuadEdgeEffect::Create(GrRandomColor(d->fRandom),
675 GrTest::TestMatrix(d->fRandom),
676 d->fRandom->nextBool()) : nullptr;
677 }
678
679 ///////////////////////////////////////////////////////////////////////////////
680
681 bool GrAAConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
682 return (args.fShaderCaps->shaderDerivativeSupport() && args.fAntiAlias &&
683 args.fStroke->isFillStyle() && !args.fPath->isInverseFillType() &&
684 args.fPath->isConvex());
685 }
686
687 // extract the result vertices and indices from the GrAAConvexTessellator
688 static void extract_verts(const GrAAConvexTessellator& tess,
689 void* vertices,
690 size_t vertexStride,
691 GrColor color,
692 uint16_t* idxs,
693 bool tweakAlphaForCoverage) {
694 intptr_t verts = reinterpret_cast<intptr_t>(vertices);
695
696 for (int i = 0; i < tess.numPts(); ++i) {
697 *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i);
698 }
699
700 // Make 'verts' point to the colors
701 verts += sizeof(SkPoint);
702 for (int i = 0; i < tess.numPts(); ++i) {
703 if (tweakAlphaForCoverage) {
704 SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255);
705 unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i));
706 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, s cale);
707 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor;
708 } else {
709 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color;
710 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor) ) =
711 tess.coverage(i);
712 }
713 }
714
715 for (int i = 0; i < tess.numIndices(); ++i) {
716 idxs[i] = tess.index(i);
717 }
718 }
719
720 static const GrGeometryProcessor* create_fill_gp(bool tweakAlphaForCoverage,
721 const SkMatrix& viewMatrix,
722 bool usesLocalCoords,
723 bool coverageIgnored) {
724 using namespace GrDefaultGeoProcFactory;
725
726 Color color(Color::kAttribute_Type);
727 Coverage::Type coverageType;
728 // TODO remove coverage if coverage is ignored
729 /*if (coverageIgnored) {
730 coverageType = Coverage::kNone_Type;
731 } else*/ if (tweakAlphaForCoverage) {
732 coverageType = Coverage::kSolid_Type;
733 } else {
734 coverageType = Coverage::kAttribute_Type;
735 }
736 Coverage coverage(coverageType);
737 LocalCoords localCoords(usesLocalCoords ? LocalCoords::kUsePosition_Type :
738 LocalCoords::kUnused_Type);
739 return CreateForDeviceSpace(color, coverage, localCoords, viewMatrix);
740 }
741
742 class AAConvexPathBatch : public GrVertexBatch {
743 public:
744 struct Geometry {
745 GrColor fColor;
746 SkMatrix fViewMatrix;
747 SkPath fPath;
748 };
749
750 static GrDrawBatch* Create(const Geometry& geometry) { return new AAConvexPa thBatch(geometry); }
751
752 const char* name() const override { return "AAConvexBatch"; }
753
754 void getInvariantOutputColor(GrInitInvariantOutput* out) const override {
755 // When this is called on a batch, there is only one geometry bundle
756 out->setKnownFourComponents(fGeoData[0].fColor);
757 }
758 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override {
759 out->setUnknownSingleComponent();
760 }
761
762 private:
763
764 void initBatchTracker(const GrPipelineOptimizations& opt) override {
765 // Handle any color overrides
766 if (!opt.readsColor()) {
767 fGeoData[0].fColor = GrColor_ILLEGAL;
768 }
769 opt.getOverrideColorIfSet(&fGeoData[0].fColor);
770
771 // setup batch properties
772 fBatch.fColorIgnored = !opt.readsColor();
773 fBatch.fColor = fGeoData[0].fColor;
774 fBatch.fUsesLocalCoords = opt.readsLocalCoords();
775 fBatch.fCoverageIgnored = !opt.readsCoverage();
776 fBatch.fLinesOnly = SkPath::kLine_SegmentMask == fGeoData[0].fPath.getSe gmentMasks();
777 fBatch.fCanTweakAlphaForCoverage = opt.canTweakAlphaForCoverage();
778 }
779
780 void prepareLinesOnlyDraws(Target* target) {
781 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage();
782
783 // Setup GrGeometryProcessor
784 SkAutoTUnref<const GrGeometryProcessor> gp(create_fill_gp(canTweakAlphaF orCoverage,
785 this->viewMatr ix(),
786 this->usesLoca lCoords(),
787 this->coverage Ignored()));
788 if (!gp) {
789 SkDebugf("Could not create GrGeometryProcessor\n");
790 return;
791 }
792
793 target->initDraw(gp, this->pipeline());
794
795 size_t vertexStride = gp->getVertexStride();
796
797 SkASSERT(canTweakAlphaForCoverage ?
798 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAt tr) :
799 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCo verageAttr));
800
801 GrAAConvexTessellator tess;
802
803 int instanceCount = fGeoData.count();
804
805 for (int i = 0; i < instanceCount; i++) {
806 tess.rewind();
807
808 Geometry& args = fGeoData[i];
809
810 if (!tess.tessellate(args.fViewMatrix, args.fPath)) {
811 continue;
812 }
813
814 const GrVertexBuffer* vertexBuffer;
815 int firstVertex;
816
817 void* verts = target->makeVertexSpace(vertexStride, tess.numPts(), & vertexBuffer,
818 &firstVertex);
819 if (!verts) {
820 SkDebugf("Could not allocate vertices\n");
821 return;
822 }
823
824 const GrIndexBuffer* indexBuffer;
825 int firstIndex;
826
827 uint16_t* idxs = target->makeIndexSpace(tess.numIndices(), &indexBuf fer, &firstIndex);
828 if (!idxs) {
829 SkDebugf("Could not allocate indices\n");
830 return;
831 }
832
833 extract_verts(tess, verts, vertexStride, args.fColor, idxs, canTweak AlphaForCoverage);
834
835 GrVertices info;
836 info.initIndexed(kTriangles_GrPrimitiveType,
837 vertexBuffer, indexBuffer,
838 firstVertex, firstIndex,
839 tess.numPts(), tess.numIndices());
840 target->draw(info);
841 }
842 }
843
844 void onPrepareDraws(Target* target) override {
845 #ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
846 if (this->linesOnly()) {
847 this->prepareLinesOnlyDraws(target);
848 return;
849 }
850 #endif
851
852 int instanceCount = fGeoData.count();
853
854 SkMatrix invert;
855 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) {
856 SkDebugf("Could not invert viewmatrix\n");
857 return;
858 }
859
860 // Setup GrGeometryProcessor
861 SkAutoTUnref<GrGeometryProcessor> quadProcessor(
862 QuadEdgeEffect::Create(this->color(), invert, this->usesLocalCoo rds()));
863
864 target->initDraw(quadProcessor, this->pipeline());
865
866 // TODO generate all segments for all paths and use one vertex buffer
867 for (int i = 0; i < instanceCount; i++) {
868 Geometry& args = fGeoData[i];
869
870 // We use the fact that SkPath::transform path does subdivision base d on
871 // perspective. Otherwise, we apply the view matrix when copying to the
872 // segment representation.
873 const SkMatrix* viewMatrix = &args.fViewMatrix;
874 if (viewMatrix->hasPerspective()) {
875 args.fPath.transform(*viewMatrix);
876 viewMatrix = &SkMatrix::I();
877 }
878
879 int vertexCount;
880 int indexCount;
881 enum {
882 kPreallocSegmentCnt = 512 / sizeof(Segment),
883 kPreallocDrawCnt = 4,
884 };
885 SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
886 SkPoint fanPt;
887
888 if (!get_segments(args.fPath, *viewMatrix, &segments, &fanPt, &verte xCount,
889 &indexCount)) {
890 continue;
891 }
892
893 const GrVertexBuffer* vertexBuffer;
894 int firstVertex;
895
896 size_t vertexStride = quadProcessor->getVertexStride();
897 QuadVertex* verts = reinterpret_cast<QuadVertex*>(target->makeVertex Space(
898 vertexStride, vertexCount, &vertexBuffer, &firstVertex));
899
900 if (!verts) {
901 SkDebugf("Could not allocate vertices\n");
902 return;
903 }
904
905 const GrIndexBuffer* indexBuffer;
906 int firstIndex;
907
908 uint16_t *idxs = target->makeIndexSpace(indexCount, &indexBuffer, &f irstIndex);
909 if (!idxs) {
910 SkDebugf("Could not allocate indices\n");
911 return;
912 }
913
914 SkSTArray<kPreallocDrawCnt, Draw, true> draws;
915 create_vertices(segments, fanPt, &draws, verts, idxs);
916
917 GrVertices vertices;
918
919 for (int i = 0; i < draws.count(); ++i) {
920 const Draw& draw = draws[i];
921 vertices.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, i ndexBuffer,
922 firstVertex, firstIndex, draw.fVertexCnt, d raw.fIndexCnt);
923 target->draw(vertices);
924 firstVertex += draw.fVertexCnt;
925 firstIndex += draw.fIndexCnt;
926 }
927 }
928 }
929
930 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; }
931
932 AAConvexPathBatch(const Geometry& geometry) {
933 this->initClassID<AAConvexPathBatch>();
934 fGeoData.push_back(geometry);
935
936 // compute bounds
937 fBounds = geometry.fPath.getBounds();
938 geometry.fViewMatrix.mapRect(&fBounds);
939 }
940
941 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
942 AAConvexPathBatch* that = t->cast<AAConvexPathBatch>();
943 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pi peline(),
944 that->bounds(), caps)) {
945 return false;
946 }
947
948 if (this->color() != that->color()) {
949 return false;
950 }
951
952 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords());
953 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->vi ewMatrix())) {
954 return false;
955 }
956
957 if (this->linesOnly() != that->linesOnly()) {
958 return false;
959 }
960
961 // In the event of two batches, one who can tweak, one who cannot, we ju st fall back to
962 // not tweaking
963 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage() ) {
964 fBatch.fCanTweakAlphaForCoverage = false;
965 }
966
967 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()) ;
968 this->joinBounds(that->bounds());
969 return true;
970 }
971
972 GrColor color() const { return fBatch.fColor; }
973 bool linesOnly() const { return fBatch.fLinesOnly; }
974 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; }
975 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCover age; }
976 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; }
977 bool coverageIgnored() const { return fBatch.fCoverageIgnored; }
978
979 struct BatchTracker {
980 GrColor fColor;
981 bool fUsesLocalCoords;
982 bool fColorIgnored;
983 bool fCoverageIgnored;
984 bool fLinesOnly;
985 bool fCanTweakAlphaForCoverage;
986 };
987
988 BatchTracker fBatch;
989 SkSTArray<1, Geometry, true> fGeoData;
990 };
991
992 bool GrAAConvexPathRenderer::onDrawPath(const DrawPathArgs& args) {
993 if (args.fPath->isEmpty()) {
994 return true;
995 }
996
997 AAConvexPathBatch::Geometry geometry;
998 geometry.fColor = args.fColor;
999 geometry.fViewMatrix = *args.fViewMatrix;
1000 geometry.fPath = *args.fPath;
1001
1002 SkAutoTUnref<GrDrawBatch> batch(AAConvexPathBatch::Create(geometry));
1003 args.fTarget->drawBatch(*args.fPipelineBuilder, batch);
1004
1005 return true;
1006
1007 }
1008
1009 //////////////////////////////////////////////////////////////////////////////// ///////////////////
1010
1011 #ifdef GR_TEST_UTILS
1012
1013 DRAW_BATCH_TEST_DEFINE(AAConvexPathBatch) {
1014 AAConvexPathBatch::Geometry geometry;
1015 geometry.fColor = GrRandomColor(random);
1016 geometry.fViewMatrix = GrTest::TestMatrixInvertible(random);
1017 geometry.fPath = GrTest::TestPathConvex(random);
1018
1019 return AAConvexPathBatch::Create(geometry);
1020 }
1021
1022 #endif
OLDNEW
« no previous file with comments | « src/gpu/GrAAConvexPathRenderer.h ('k') | src/gpu/GrAAConvexTessellator.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698