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 "GrBatch.h" 13#include "GrBatchTarget.h" 14#include "GrBatchTest.h" 15#include "GrContext.h" 16#include "GrDefaultGeoProcFactory.h" 17#include "GrDrawTargetCaps.h" 18#include "GrGeometryProcessor.h" 19#include "GrInvariantOutput.h" 20#include "GrPathUtils.h" 21#include "GrProcessor.h" 22#include "GrPipelineBuilder.h" 23#include "GrStrokeInfo.h" 24#include "SkGeometry.h" 25#include "SkString.h" 26#include "SkTraceEvent.h" 27#include "gl/GrGLProcessor.h" 28#include "gl/GrGLSL.h" 29#include "gl/GrGLGeometryProcessor.h" 30#include "gl/builders/GrGLProgramBuilder.h" 31 32GrAAConvexPathRenderer::GrAAConvexPathRenderer() { 33} 34 35struct 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 64typedef SkTArray<Segment, true> SegmentArray; 65 66static 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 117static void compute_vectors(SegmentArray* segments, 118 SkPoint* fanPt, 119 SkPath::Direction 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 == SkPath::kCCW_Direction) { 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 172struct 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 186static const SkScalar kClose = (SK_Scalar1 / 16); 187static const SkScalar kCloseSqd = SkScalarMul(kClose, kClose); 188 189static 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 215static inline bool get_direction(const SkPath& path, const SkMatrix& m, SkPath::Direction* dir) { 216 if (!path.cheapComputeDirection(dir)) { 217 return false; 218 } 219 // check whether m reverses the orientation 220 SkASSERT(!m.hasPerspective()); 221 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) - 222 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY)); 223 if (det2x2 < 0) { 224 *dir = SkPath::OppositeDirection(*dir); 225 } 226 return true; 227} 228 229static inline void add_line_to_segment(const SkPoint& pt, 230 SegmentArray* segments) { 231 segments->push_back(); 232 segments->back().fType = Segment::kLine; 233 segments->back().fPts[0] = pt; 234} 235 236static inline void add_quad_segment(const SkPoint pts[3], 237 SegmentArray* segments) { 238 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) { 239 if (pts[0] != pts[2]) { 240 add_line_to_segment(pts[2], segments); 241 } 242 } else { 243 segments->push_back(); 244 segments->back().fType = Segment::kQuad; 245 segments->back().fPts[0] = pts[1]; 246 segments->back().fPts[1] = pts[2]; 247 } 248} 249 250static inline void add_cubic_segments(const SkPoint pts[4], 251 SkPath::Direction dir, 252 SegmentArray* segments) { 253 SkSTArray<15, SkPoint, true> quads; 254 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads); 255 int count = quads.count(); 256 for (int q = 0; q < count; q += 3) { 257 add_quad_segment(&quads[q], segments); 258 } 259} 260 261static bool get_segments(const SkPath& path, 262 const SkMatrix& m, 263 SegmentArray* segments, 264 SkPoint* fanPt, 265 int* vCount, 266 int* iCount) { 267 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 DegenerateTestData degenerateData; 276 SkPath::Direction dir; 277 // get_direction can fail for some degenerate paths. 278 if (!get_direction(path, m, &dir)) { 279 return false; 280 } 281 282 for (;;) { 283 SkPoint pts[4]; 284 SkPath::Verb verb = iter.next(pts); 285 switch (verb) { 286 case SkPath::kMove_Verb: 287 m.mapPoints(pts, 1); 288 update_degenerate_test(°enerateData, pts[0]); 289 break; 290 case SkPath::kLine_Verb: { 291 m.mapPoints(&pts[1], 1); 292 update_degenerate_test(°enerateData, pts[1]); 293 add_line_to_segment(pts[1], segments); 294 break; 295 } 296 case SkPath::kQuad_Verb: 297 m.mapPoints(pts, 3); 298 update_degenerate_test(°enerateData, pts[1]); 299 update_degenerate_test(°enerateData, pts[2]); 300 add_quad_segment(pts, segments); 301 break; 302 case SkPath::kConic_Verb: { 303 m.mapPoints(pts, 3); 304 SkScalar weight = iter.conicWeight(); 305 SkAutoConicToQuads converter; 306 const SkPoint* quadPts = converter.computeQuads(pts, weight, 0.5f); 307 for (int i = 0; i < converter.countQuads(); ++i) { 308 update_degenerate_test(°enerateData, quadPts[2*i + 1]); 309 update_degenerate_test(°enerateData, quadPts[2*i + 2]); 310 add_quad_segment(quadPts + 2*i, segments); 311 } 312 break; 313 } 314 case SkPath::kCubic_Verb: { 315 m.mapPoints(pts, 4); 316 update_degenerate_test(°enerateData, pts[1]); 317 update_degenerate_test(°enerateData, pts[2]); 318 update_degenerate_test(°enerateData, pts[3]); 319 add_cubic_segments(pts, dir, segments); 320 break; 321 }; 322 case SkPath::kDone_Verb: 323 if (degenerateData.isDegenerate()) { 324 return false; 325 } else { 326 compute_vectors(segments, fanPt, dir, vCount, iCount); 327 return true; 328 } 329 default: 330 break; 331 } 332 } 333} 334 335struct QuadVertex { 336 SkPoint fPos; 337 SkPoint fUV; 338 SkScalar fD0; 339 SkScalar fD1; 340}; 341 342struct Draw { 343 Draw() : fVertexCnt(0), fIndexCnt(0) {} 344 int fVertexCnt; 345 int fIndexCnt; 346}; 347 348typedef SkTArray<Draw, true> DrawArray; 349 350static void create_vertices(const SegmentArray& segments, 351 const SkPoint& fanPt, 352 DrawArray* draws, 353 QuadVertex* verts, 354 uint16_t* idxs) { 355 Draw* draw = &draws->push_back(); 356 // alias just to make vert/index assignments easier to read. 357 int* v = &draw->fVertexCnt; 358 int* i = &draw->fIndexCnt; 359 360 int count = segments.count(); 361 for (int a = 0; a < count; ++a) { 362 const Segment& sega = segments[a]; 363 int b = (a + 1) % count; 364 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 int vCount = 4; 369 if (Segment::kLine == segb.fType) { 370 vCount += 5; 371 } else { 372 vCount += 6; 373 } 374 if (draw->fVertexCnt + vCount > (1 << 16)) { 375 verts += *v; 376 idxs += *i; 377 draw = &draws->push_back(); 378 v = &draw->fVertexCnt; 379 i = &draw->fIndexCnt; 380 } 381 382 // FIXME: These tris are inset in the 1 unit arc around the corner 383 verts[*v + 0].fPos = sega.endPt(); 384 verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm(); 385 verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid; 386 verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0]; 387 verts[*v + 0].fUV.set(0,0); 388 verts[*v + 1].fUV.set(0,-SK_Scalar1); 389 verts[*v + 2].fUV.set(0,-SK_Scalar1); 390 verts[*v + 3].fUV.set(0,-SK_Scalar1); 391 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1; 392 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1; 393 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1; 394 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1; 395 396 idxs[*i + 0] = *v + 0; 397 idxs[*i + 1] = *v + 2; 398 idxs[*i + 2] = *v + 1; 399 idxs[*i + 3] = *v + 0; 400 idxs[*i + 4] = *v + 3; 401 idxs[*i + 5] = *v + 2; 402 403 *v += 4; 404 *i += 6; 405 406 if (Segment::kLine == segb.fType) { 407 verts[*v + 0].fPos = fanPt; 408 verts[*v + 1].fPos = sega.endPt(); 409 verts[*v + 2].fPos = segb.fPts[0]; 410 411 verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0]; 412 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 SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos, 417 verts[*v + 2].fPos); 418 verts[*v + 0].fUV.set(0, dist); 419 verts[*v + 1].fUV.set(0, 0); 420 verts[*v + 2].fUV.set(0, 0); 421 verts[*v + 3].fUV.set(0, -SK_Scalar1); 422 verts[*v + 4].fUV.set(0, -SK_Scalar1); 423 424 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1; 425 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1; 426 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1; 427 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1; 428 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1; 429 430 idxs[*i + 0] = *v + 3; 431 idxs[*i + 1] = *v + 1; 432 idxs[*i + 2] = *v + 2; 433 434 idxs[*i + 3] = *v + 4; 435 idxs[*i + 4] = *v + 3; 436 idxs[*i + 5] = *v + 2; 437 438 *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 if (count >= 3) { 444 idxs[*i + 0] = *v + 0; 445 idxs[*i + 1] = *v + 2; 446 idxs[*i + 2] = *v + 1; 447 448 *i += 3; 449 } 450 451 *v += 5; 452 } else { 453 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]}; 454 455 SkVector midVec = segb.fNorms[0] + segb.fNorms[1]; 456 midVec.normalize(); 457 458 verts[*v + 0].fPos = fanPt; 459 verts[*v + 1].fPos = qpts[0]; 460 verts[*v + 2].fPos = qpts[2]; 461 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0]; 462 verts[*v + 4].fPos = qpts[2] + segb.fNorms[1]; 463 verts[*v + 5].fPos = qpts[1] + midVec; 464 465 SkScalar c = segb.fNorms[0].dot(qpts[0]); 466 verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c; 467 verts[*v + 1].fD0 = 0.f; 468 verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c; 469 verts[*v + 3].fD0 = -SK_ScalarMax/100; 470 verts[*v + 4].fD0 = -SK_ScalarMax/100; 471 verts[*v + 5].fD0 = -SK_ScalarMax/100; 472 473 c = segb.fNorms[1].dot(qpts[2]); 474 verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c; 475 verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c; 476 verts[*v + 2].fD1 = 0.f; 477 verts[*v + 3].fD1 = -SK_ScalarMax/100; 478 verts[*v + 4].fD1 = -SK_ScalarMax/100; 479 verts[*v + 5].fD1 = -SK_ScalarMax/100; 480 481 GrPathUtils::QuadUVMatrix toUV(qpts); 482 toUV.apply<6, sizeof(QuadVertex), sizeof(SkPoint)>(verts + *v); 483 484 idxs[*i + 0] = *v + 3; 485 idxs[*i + 1] = *v + 1; 486 idxs[*i + 2] = *v + 2; 487 idxs[*i + 3] = *v + 4; 488 idxs[*i + 4] = *v + 3; 489 idxs[*i + 5] = *v + 2; 490 491 idxs[*i + 6] = *v + 5; 492 idxs[*i + 7] = *v + 3; 493 idxs[*i + 8] = *v + 4; 494 495 *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 if (count >= 3) { 501 idxs[*i + 0] = *v + 0; 502 idxs[*i + 1] = *v + 2; 503 idxs[*i + 2] = *v + 1; 504 505 *i += 3; 506 } 507 508 *v += 6; 509 } 510 } 511} 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 525class QuadEdgeEffect : public GrGeometryProcessor { 526public: 527 528 static GrGeometryProcessor* Create(GrColor color, const SkMatrix& localMatrix) { 529 return SkNEW_ARGS(QuadEdgeEffect, (color, localMatrix)); 530 } 531 532 virtual ~QuadEdgeEffect() {} 533 534 const char* name() const override { return "QuadEdge"; } 535 536 const Attribute* inPosition() const { return fInPosition; } 537 const Attribute* inQuadEdge() const { return fInQuadEdge; } 538 GrColor color() const { return fColor; } 539 const SkMatrix& localMatrix() const { return fLocalMatrix; } 540 541 class GLProcessor : public GrGLGeometryProcessor { 542 public: 543 GLProcessor(const GrGeometryProcessor&, 544 const GrBatchTracker&) 545 : fColor(GrColor_ILLEGAL) {} 546 547 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 548 const QuadEdgeEffect& qe = args.fGP.cast<QuadEdgeEffect>(); 549 GrGLGPBuilder* pb = args.fPB; 550 GrGLVertexBuilder* vsBuilder = pb->getVertexShaderBuilder(); 551 552 // emit attributes 553 vsBuilder->emitAttributes(qe); 554 555 GrGLVertToFrag v(kVec4f_GrSLType); 556 args.fPB->addVarying("QuadEdge", &v); 557 vsBuilder->codeAppendf("%s = %s;", v.vsOut(), qe.inQuadEdge()->fName); 558 559 const BatchTracker& local = args.fBT.cast<BatchTracker>(); 560 561 // Setup pass through color 562 this->setupColorPassThrough(pb, local.fInputColorType, args.fOutputColor, NULL, 563 &fColorUniform); 564 565 // Setup position 566 this->setupPosition(pb, gpArgs, qe.inPosition()->fName); 567 568 // emit transforms 569 this->emitTransforms(args.fPB, gpArgs->fPositionVar, qe.inPosition()->fName, 570 qe.localMatrix(), args.fTransformsIn, args.fTransformsOut); 571 572 GrGLFragmentBuilder* fsBuilder = args.fPB->getFragmentShaderBuilder(); 573 574 SkAssertResult(fsBuilder->enableFeature( 575 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); 576 fsBuilder->codeAppendf("float edgeAlpha;"); 577 578 // keep the derivative instructions outside the conditional 579 fsBuilder->codeAppendf("vec2 duvdx = dFdx(%s.xy);", v.fsIn()); 580 fsBuilder->codeAppendf("vec2 duvdy = dFdy(%s.xy);", v.fsIn()); 581 fsBuilder->codeAppendf("if (%s.z > 0.0 && %s.w > 0.0) {", v.fsIn(), v.fsIn()); 582 // today we know z and w are in device space. We could use derivatives 583 fsBuilder->codeAppendf("edgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);", v.fsIn(), 584 v.fsIn()); 585 fsBuilder->codeAppendf ("} else {"); 586 fsBuilder->codeAppendf("vec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y," 587 " 2.0*%s.x*duvdy.x - duvdy.y);", 588 v.fsIn(), v.fsIn()); 589 fsBuilder->codeAppendf("edgeAlpha = (%s.x*%s.x - %s.y);", v.fsIn(), v.fsIn(), 590 v.fsIn()); 591 fsBuilder->codeAppendf("edgeAlpha = " 592 "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);}"); 593 594 fsBuilder->codeAppendf("%s = vec4(edgeAlpha);", args.fOutputCoverage); 595 } 596 597 static inline void GenKey(const GrGeometryProcessor& gp, 598 const GrBatchTracker& bt, 599 const GrGLSLCaps&, 600 GrProcessorKeyBuilder* b) { 601 const BatchTracker& local = bt.cast<BatchTracker>(); 602 const QuadEdgeEffect& qee = gp.cast<QuadEdgeEffect>(); 603 uint32_t key = local.fInputColorType << 16; 604 key |= local.fUsesLocalCoords && qee.localMatrix().hasPerspective() ? 0x1 : 0x0; 605 b->add32(key); 606 } 607 608 virtual void setData(const GrGLProgramDataManager& pdman, 609 const GrPrimitiveProcessor& gp, 610 const GrBatchTracker& bt) override { 611 const BatchTracker& local = bt.cast<BatchTracker>(); 612 if (kUniform_GrGPInput == local.fInputColorType && local.fColor != fColor) { 613 GrGLfloat c[4]; 614 GrColorToRGBAFloat(local.fColor, c); 615 pdman.set4fv(fColorUniform, 1, c); 616 fColor = local.fColor; 617 } 618 } 619 620 void setTransformData(const GrPrimitiveProcessor& primProc, 621 const GrGLProgramDataManager& pdman, 622 int index, 623 const SkTArray<const GrCoordTransform*, true>& transforms) override { 624 this->setTransformDataHelper<QuadEdgeEffect>(primProc, pdman, index, transforms); 625 } 626 627 private: 628 GrColor fColor; 629 UniformHandle fColorUniform; 630 631 typedef GrGLGeometryProcessor INHERITED; 632 }; 633 634 virtual void getGLProcessorKey(const GrBatchTracker& bt, 635 const GrGLSLCaps& caps, 636 GrProcessorKeyBuilder* b) const override { 637 GLProcessor::GenKey(*this, bt, caps, b); 638 } 639 640 virtual GrGLPrimitiveProcessor* createGLInstance(const GrBatchTracker& bt, 641 const GrGLSLCaps&) const override { 642 return SkNEW_ARGS(GLProcessor, (*this, bt)); 643 } 644 645 void initBatchTracker(GrBatchTracker* bt, const GrPipelineInfo& init) const override { 646 BatchTracker* local = bt->cast<BatchTracker>(); 647 local->fInputColorType = GetColorInputType(&local->fColor, this->color(), init, false); 648 local->fUsesLocalCoords = init.fUsesLocalCoords; 649 } 650 651private: 652 QuadEdgeEffect(GrColor color, const SkMatrix& localMatrix) 653 : fColor(color) 654 , fLocalMatrix(localMatrix) { 655 this->initClassID<QuadEdgeEffect>(); 656 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType)); 657 fInQuadEdge = &this->addVertexAttrib(Attribute("inQuadEdge", kVec4f_GrVertexAttribType)); 658 } 659 660 struct BatchTracker { 661 GrGPInput fInputColorType; 662 GrColor fColor; 663 bool fUsesLocalCoords; 664 }; 665 666 const Attribute* fInPosition; 667 const Attribute* fInQuadEdge; 668 GrColor fColor; 669 SkMatrix fLocalMatrix; 670 671 GR_DECLARE_GEOMETRY_PROCESSOR_TEST; 672 673 typedef GrGeometryProcessor INHERITED; 674}; 675 676GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect); 677 678GrGeometryProcessor* QuadEdgeEffect::TestCreate(SkRandom* random, 679 GrContext*, 680 const GrDrawTargetCaps& caps, 681 GrTexture*[]) { 682 // Doesn't work without derivative instructions. 683 return caps.shaderCaps()->shaderDerivativeSupport() ? 684 QuadEdgeEffect::Create(GrRandomColor(random), 685 GrTest::TestMatrix(random)) : NULL; 686} 687 688/////////////////////////////////////////////////////////////////////////////// 689 690bool GrAAConvexPathRenderer::canDrawPath(const GrDrawTarget* target, 691 const GrPipelineBuilder*, 692 const SkMatrix& viewMatrix, 693 const SkPath& path, 694 const GrStrokeInfo& stroke, 695 bool antiAlias) const { 696 return (target->caps()->shaderCaps()->shaderDerivativeSupport() && antiAlias && 697 stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex()); 698} 699 700// extract the result vertices and indices from the GrAAConvexTessellator 701static void extract_verts(const GrAAConvexTessellator& tess, 702 void* vertices, 703 size_t vertexStride, 704 GrColor color, 705 uint16_t* idxs, 706 bool tweakAlphaForCoverage) { 707 intptr_t verts = reinterpret_cast<intptr_t>(vertices); 708 709 for (int i = 0; i < tess.numPts(); ++i) { 710 *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i); 711 } 712 713 // Make 'verts' point to the colors 714 verts += sizeof(SkPoint); 715 for (int i = 0; i < tess.numPts(); ++i) { 716 SkASSERT(tess.depth(i) >= -0.5f && tess.depth(i) <= 0.5f); 717 if (tweakAlphaForCoverage) { 718 SkASSERT(SkScalarRoundToInt(255.0f * (tess.depth(i) + 0.5f)) <= 255); 719 unsigned scale = SkScalarRoundToInt(255.0f * (tess.depth(i) + 0.5f)); 720 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); 721 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; 722 } else { 723 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; 724 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 725 tess.depth(i) + 0.5f; 726 } 727 } 728 729 for (int i = 0; i < tess.numIndices(); ++i) { 730 idxs[i] = tess.index(i); 731 } 732} 733 734static const GrGeometryProcessor* create_fill_gp(bool tweakAlphaForCoverage, 735 const SkMatrix& localMatrix) { 736 uint32_t flags = GrDefaultGeoProcFactory::kColor_GPType; 737 if (!tweakAlphaForCoverage) { 738 flags |= GrDefaultGeoProcFactory::kCoverage_GPType; 739 } 740 741 return GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, SkMatrix::I(), localMatrix); 742} 743 744class AAConvexPathBatch : public GrBatch { 745public: 746 struct Geometry { 747 GrColor fColor; 748 SkMatrix fViewMatrix; 749 SkPath fPath; 750 }; 751 752 static GrBatch* Create(const Geometry& geometry) { 753 return SkNEW_ARGS(AAConvexPathBatch, (geometry)); 754 } 755 756 const char* name() const override { return "AAConvexBatch"; } 757 758 void getInvariantOutputColor(GrInitInvariantOutput* out) const override { 759 // When this is called on a batch, there is only one geometry bundle 760 out->setKnownFourComponents(fGeoData[0].fColor); 761 } 762 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { 763 out->setUnknownSingleComponent(); 764 } 765 766 void initBatchTracker(const GrPipelineInfo& init) override { 767 // Handle any color overrides 768 if (init.fColorIgnored) { 769 fGeoData[0].fColor = GrColor_ILLEGAL; 770 } else if (GrColor_ILLEGAL != init.fOverrideColor) { 771 fGeoData[0].fColor = init.fOverrideColor; 772 } 773 774 // setup batch properties 775 fBatch.fColorIgnored = init.fColorIgnored; 776 fBatch.fColor = fGeoData[0].fColor; 777 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; 778 fBatch.fCoverageIgnored = init.fCoverageIgnored; 779 fBatch.fLinesOnly = SkPath::kLine_SegmentMask == fGeoData[0].fPath.getSegmentMasks(); 780 fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage; 781 } 782 783 void generateGeometryLinesOnly(GrBatchTarget* batchTarget, const GrPipeline* pipeline) { 784 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage(); 785 786 SkMatrix invert; 787 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) { 788 SkDebugf("Could not invert viewmatrix\n"); 789 return; 790 } 791 792 // Setup GrGeometryProcessor 793 SkAutoTUnref<const GrGeometryProcessor> gp( 794 create_fill_gp(canTweakAlphaForCoverage, invert)); 795 796 batchTarget->initDraw(gp, pipeline); 797 798 // TODO remove this when batch is everywhere 799 GrPipelineInfo init; 800 init.fColorIgnored = fBatch.fColorIgnored; 801 init.fOverrideColor = GrColor_ILLEGAL; 802 init.fCoverageIgnored = fBatch.fCoverageIgnored; 803 init.fUsesLocalCoords = this->usesLocalCoords(); 804 gp->initBatchTracker(batchTarget->currentBatchTracker(), init); 805 806 size_t vertexStride = gp->getVertexStride(); 807 808 SkASSERT(canTweakAlphaForCoverage ? 809 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) : 810 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr)); 811 812 GrAAConvexTessellator tess; 813 814 int instanceCount = fGeoData.count(); 815 816 for (int i = 0; i < instanceCount; i++) { 817 tess.rewind(); 818 819 Geometry& args = fGeoData[i]; 820 821 if (!tess.tessellate(args.fViewMatrix, args.fPath)) { 822 continue; 823 } 824 825 const GrVertexBuffer* vertexBuffer; 826 int firstVertex; 827 828 void* verts = batchTarget->makeVertSpace(vertexStride, tess.numPts(), 829 &vertexBuffer, &firstVertex); 830 if (!verts) { 831 SkDebugf("Could not allocate vertices\n"); 832 return; 833 } 834 835 const GrIndexBuffer* indexBuffer; 836 int firstIndex; 837 838 uint16_t* idxs = batchTarget->makeIndexSpace(tess.numIndices(), 839 &indexBuffer, &firstIndex); 840 if (!idxs) { 841 SkDebugf("Could not allocate indices\n"); 842 return; 843 } 844 845 extract_verts(tess, verts, vertexStride, args.fColor, idxs, canTweakAlphaForCoverage); 846 847 GrVertices info; 848 info.initIndexed(kTriangles_GrPrimitiveType, 849 vertexBuffer, indexBuffer, 850 firstVertex, firstIndex, 851 tess.numPts(), tess.numIndices()); 852 batchTarget->draw(info); 853 } 854 } 855 856 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override { 857#ifndef SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS 858 if (this->linesOnly()) { 859 this->generateGeometryLinesOnly(batchTarget, pipeline); 860 return; 861 } 862#endif 863 864 int instanceCount = fGeoData.count(); 865 866 SkMatrix invert; 867 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) { 868 SkDebugf("Could not invert viewmatrix\n"); 869 return; 870 } 871 872 // Setup GrGeometryProcessor 873 SkAutoTUnref<GrGeometryProcessor> quadProcessor(QuadEdgeEffect::Create(this->color(), 874 invert)); 875 876 batchTarget->initDraw(quadProcessor, pipeline); 877 878 // TODO remove this when batch is everywhere 879 GrPipelineInfo init; 880 init.fColorIgnored = fBatch.fColorIgnored; 881 init.fOverrideColor = GrColor_ILLEGAL; 882 init.fCoverageIgnored = fBatch.fCoverageIgnored; 883 init.fUsesLocalCoords = this->usesLocalCoords(); 884 quadProcessor->initBatchTracker(batchTarget->currentBatchTracker(), init); 885 886 // TODO generate all segments for all paths and use one vertex buffer 887 for (int i = 0; i < instanceCount; i++) { 888 Geometry& args = fGeoData[i]; 889 890 // We use the fact that SkPath::transform path does subdivision based on 891 // perspective. Otherwise, we apply the view matrix when copying to the 892 // segment representation. 893 const SkMatrix* viewMatrix = &args.fViewMatrix; 894 if (viewMatrix->hasPerspective()) { 895 args.fPath.transform(*viewMatrix); 896 viewMatrix = &SkMatrix::I(); 897 } 898 899 int vertexCount; 900 int indexCount; 901 enum { 902 kPreallocSegmentCnt = 512 / sizeof(Segment), 903 kPreallocDrawCnt = 4, 904 }; 905 SkSTArray<kPreallocSegmentCnt, Segment, true> segments; 906 SkPoint fanPt; 907 908 if (!get_segments(args.fPath, *viewMatrix, &segments, &fanPt, &vertexCount, 909 &indexCount)) { 910 continue; 911 } 912 913 const GrVertexBuffer* vertexBuffer; 914 int firstVertex; 915 916 size_t vertexStride = quadProcessor->getVertexStride(); 917 QuadVertex* verts = reinterpret_cast<QuadVertex*>(batchTarget->makeVertSpace( 918 vertexStride, vertexCount, &vertexBuffer, &firstVertex)); 919 920 if (!verts) { 921 SkDebugf("Could not allocate vertices\n"); 922 return; 923 } 924 925 const GrIndexBuffer* indexBuffer; 926 int firstIndex; 927 928 uint16_t *idxs = batchTarget->makeIndexSpace(indexCount, &indexBuffer, &firstIndex); 929 if (!idxs) { 930 SkDebugf("Could not allocate indices\n"); 931 return; 932 } 933 934 SkSTArray<kPreallocDrawCnt, Draw, true> draws; 935 create_vertices(segments, fanPt, &draws, verts, idxs); 936 937 GrVertices vertices; 938 939 for (int i = 0; i < draws.count(); ++i) { 940 const Draw& draw = draws[i]; 941 vertices.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, 942 firstVertex, firstIndex, draw.fVertexCnt, draw.fIndexCnt); 943 batchTarget->draw(vertices); 944 firstVertex += draw.fVertexCnt; 945 firstIndex += draw.fIndexCnt; 946 } 947 } 948 } 949 950 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } 951 952private: 953 AAConvexPathBatch(const Geometry& geometry) { 954 this->initClassID<AAConvexPathBatch>(); 955 fGeoData.push_back(geometry); 956 957 // compute bounds 958 fBounds = geometry.fPath.getBounds(); 959 geometry.fViewMatrix.mapRect(&fBounds); 960 } 961 962 bool onCombineIfPossible(GrBatch* t) override { 963 AAConvexPathBatch* that = t->cast<AAConvexPathBatch>(); 964 965 if (this->color() != that->color()) { 966 return false; 967 } 968 969 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); 970 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 971 return false; 972 } 973 974 if (this->linesOnly() != that->linesOnly()) { 975 return false; 976 } 977 978 // In the event of two batches, one who can tweak, one who cannot, we just fall back to 979 // not tweaking 980 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) { 981 fBatch.fCanTweakAlphaForCoverage = false; 982 } 983 984 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); 985 this->joinBounds(that->bounds()); 986 return true; 987 } 988 989 GrColor color() const { return fBatch.fColor; } 990 bool linesOnly() const { return fBatch.fLinesOnly; } 991 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } 992 bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; } 993 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } 994 995 struct BatchTracker { 996 GrColor fColor; 997 bool fUsesLocalCoords; 998 bool fColorIgnored; 999 bool fCoverageIgnored; 1000 bool fLinesOnly; 1001 bool fCanTweakAlphaForCoverage; 1002 }; 1003 1004 BatchTracker fBatch; 1005 SkSTArray<1, Geometry, true> fGeoData; 1006}; 1007 1008bool GrAAConvexPathRenderer::onDrawPath(GrDrawTarget* target, 1009 GrPipelineBuilder* pipelineBuilder, 1010 GrColor color, 1011 const SkMatrix& vm, 1012 const SkPath& path, 1013 const GrStrokeInfo&, 1014 bool antiAlias) { 1015 if (path.isEmpty()) { 1016 return true; 1017 } 1018 1019 AAConvexPathBatch::Geometry geometry; 1020 geometry.fColor = color; 1021 geometry.fViewMatrix = vm; 1022 geometry.fPath = path; 1023 1024 SkAutoTUnref<GrBatch> batch(AAConvexPathBatch::Create(geometry)); 1025 target->drawBatch(pipelineBuilder, batch); 1026 1027 return true; 1028 1029} 1030 1031/////////////////////////////////////////////////////////////////////////////////////////////////// 1032 1033#ifdef GR_TEST_UTILS 1034 1035BATCH_TEST_DEFINE(AAConvexPathBatch) { 1036 AAConvexPathBatch::Geometry geometry; 1037 geometry.fColor = GrRandomColor(random); 1038 geometry.fViewMatrix = GrTest::TestMatrixInvertible(random); 1039 geometry.fPath = GrTest::TestPathConvex(random); 1040 1041 return AAConvexPathBatch::Create(geometry); 1042} 1043 1044#endif 1045