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 "GrContext.h" 12 #include "GrDrawState.h" 13 #include "GrDrawTargetCaps.h" 14 #include "GrProcessor.h" 15 #include "GrPathUtils.h" 16 #include "GrTBackendProcessorFactory.h" 17 #include "SkString.h" 18 #include "SkStrokeRec.h" 19 #include "SkTraceEvent.h" 20 21 #include "gl/builders/GrGLFullProgramBuilder.h" 22 #include "gl/GrGLProcessor.h" 23 #include "gl/GrGLSL.h" 24 #include "gl/GrGLGeometryProcessor.h" 25 26 #include "GrGeometryProcessor.h" 27 28 GrAAConvexPathRenderer::GrAAConvexPathRenderer() { 29 } 30 31 struct Segment { 32 enum { 33 // These enum values are assumed in member functions below. 34 kLine = 0, 35 kQuad = 1, 36 } fType; 37 38 // line uses one pt, quad uses 2 pts 39 SkPoint fPts[2]; 40 // normal to edge ending at each pt 41 SkVector fNorms[2]; 42 // is the corner where the previous segment meets this segment 43 // sharp. If so, fMid is a normalized bisector facing outward. 44 SkVector fMid; 45 46 int countPoints() { 47 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad); 48 return fType + 1; 49 } 50 const SkPoint& endPt() const { 51 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad); 52 return fPts[fType]; 53 }; 54 const SkPoint& endNorm() const { 55 GR_STATIC_ASSERT(0 == kLine && 1 == kQuad); 56 return fNorms[fType]; 57 }; 58 }; 59 60 typedef SkTArray<Segment, true> SegmentArray; 61 62 static void center_of_mass(const SegmentArray& segments, SkPoint* c) { 63 SkScalar area = 0; 64 SkPoint center = {0, 0}; 65 int count = segments.count(); 66 SkPoint p0 = {0, 0}; 67 if (count > 2) { 68 // We translate the polygon so that the first point is at the origin. 69 // This avoids some precision issues with small area polygons far away 70 // from the origin. 71 p0 = segments[0].endPt(); 72 SkPoint pi; 73 SkPoint pj; 74 // the first and last iteration of the below loop would compute 75 // zeros since the starting / ending point is (0,0). So instead we start 76 // at i=1 and make the last iteration i=count-2. 77 pj = segments[1].endPt() - p0; 78 for (int i = 1; i < count - 1; ++i) { 79 pi = pj; 80 const SkPoint pj = segments[i + 1].endPt() - p0; 81 82 SkScalar t = SkScalarMul(pi.fX, pj.fY) - SkScalarMul(pj.fX, pi.fY); 83 area += t; 84 center.fX += (pi.fX + pj.fX) * t; 85 center.fY += (pi.fY + pj.fY) * t; 86 87 } 88 } 89 // If the poly has no area then we instead return the average of 90 // its points. 91 if (SkScalarNearlyZero(area)) { 92 SkPoint avg; 93 avg.set(0, 0); 94 for (int i = 0; i < count; ++i) { 95 const SkPoint& pt = segments[i].endPt(); 96 avg.fX += pt.fX; 97 avg.fY += pt.fY; 98 } 99 SkScalar denom = SK_Scalar1 / count; 100 avg.scale(denom); 101 *c = avg; 102 } else { 103 area *= 3; 104 area = SkScalarDiv(SK_Scalar1, area); 105 center.fX = SkScalarMul(center.fX, area); 106 center.fY = SkScalarMul(center.fY, area); 107 // undo the translate of p0 to the origin. 108 *c = center + p0; 109 } 110 SkASSERT(!SkScalarIsNaN(c->fX) && !SkScalarIsNaN(c->fY)); 111 } 112 113 static void compute_vectors(SegmentArray* segments, 114 SkPoint* fanPt, 115 SkPath::Direction dir, 116 int* vCount, 117 int* iCount) { 118 center_of_mass(*segments, fanPt); 119 int count = segments->count(); 120 121 // Make the normals point towards the outside 122 SkPoint::Side normSide; 123 if (dir == SkPath::kCCW_Direction) { 124 normSide = SkPoint::kRight_Side; 125 } else { 126 normSide = SkPoint::kLeft_Side; 127 } 128 129 *vCount = 0; 130 *iCount = 0; 131 // compute normals at all points 132 for (int a = 0; a < count; ++a) { 133 Segment& sega = (*segments)[a]; 134 int b = (a + 1) % count; 135 Segment& segb = (*segments)[b]; 136 137 const SkPoint* prevPt = &sega.endPt(); 138 int n = segb.countPoints(); 139 for (int p = 0; p < n; ++p) { 140 segb.fNorms[p] = segb.fPts[p] - *prevPt; 141 segb.fNorms[p].normalize(); 142 segb.fNorms[p].setOrthog(segb.fNorms[p], normSide); 143 prevPt = &segb.fPts[p]; 144 } 145 if (Segment::kLine == segb.fType) { 146 *vCount += 5; 147 *iCount += 9; 148 } else { 149 *vCount += 6; 150 *iCount += 12; 151 } 152 } 153 154 // compute mid-vectors where segments meet. TODO: Detect shallow corners 155 // and leave out the wedges and close gaps by stitching segments together. 156 for (int a = 0; a < count; ++a) { 157 const Segment& sega = (*segments)[a]; 158 int b = (a + 1) % count; 159 Segment& segb = (*segments)[b]; 160 segb.fMid = segb.fNorms[0] + sega.endNorm(); 161 segb.fMid.normalize(); 162 // corner wedges 163 *vCount += 4; 164 *iCount += 6; 165 } 166 } 167 168 struct DegenerateTestData { 169 DegenerateTestData() { fStage = kInitial; } 170 bool isDegenerate() const { return kNonDegenerate != fStage; } 171 enum { 172 kInitial, 173 kPoint, 174 kLine, 175 kNonDegenerate 176 } fStage; 177 SkPoint fFirstPoint; 178 SkVector fLineNormal; 179 SkScalar fLineC; 180 }; 181 182 static const SkScalar kClose = (SK_Scalar1 / 16); 183 static const SkScalar kCloseSqd = SkScalarMul(kClose, kClose); 184 185 static void update_degenerate_test(DegenerateTestData* data, const SkPoint& pt) { 186 switch (data->fStage) { 187 case DegenerateTestData::kInitial: 188 data->fFirstPoint = pt; 189 data->fStage = DegenerateTestData::kPoint; 190 break; 191 case DegenerateTestData::kPoint: 192 if (pt.distanceToSqd(data->fFirstPoint) > kCloseSqd) { 193 data->fLineNormal = pt - data->fFirstPoint; 194 data->fLineNormal.normalize(); 195 data->fLineNormal.setOrthog(data->fLineNormal); 196 data->fLineC = -data->fLineNormal.dot(data->fFirstPoint); 197 data->fStage = DegenerateTestData::kLine; 198 } 199 break; 200 case DegenerateTestData::kLine: 201 if (SkScalarAbs(data->fLineNormal.dot(pt) + data->fLineC) > kClose) { 202 data->fStage = DegenerateTestData::kNonDegenerate; 203 } 204 case DegenerateTestData::kNonDegenerate: 205 break; 206 default: 207 SkFAIL("Unexpected degenerate test stage."); 208 } 209 } 210 211 static inline bool get_direction(const SkPath& path, const SkMatrix& m, SkPath::Direction* dir) { 212 if (!path.cheapComputeDirection(dir)) { 213 return false; 214 } 215 // check whether m reverses the orientation 216 SkASSERT(!m.hasPerspective()); 217 SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) - 218 SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY)); 219 if (det2x2 < 0) { 220 *dir = SkPath::OppositeDirection(*dir); 221 } 222 return true; 223 } 224 225 static inline void add_line_to_segment(const SkPoint& pt, 226 SegmentArray* segments, 227 SkRect* devBounds) { 228 segments->push_back(); 229 segments->back().fType = Segment::kLine; 230 segments->back().fPts[0] = pt; 231 devBounds->growToInclude(pt.fX, pt.fY); 232 } 233 234 #ifdef SK_DEBUG 235 static inline bool contains_inclusive(const SkRect& rect, const SkPoint& p) { 236 return p.fX >= rect.fLeft && p.fX <= rect.fRight && p.fY >= rect.fTop && p.fY <= rect.fBottom; 237 } 238 #endif 239 240 static inline void add_quad_segment(const SkPoint pts[3], 241 SegmentArray* segments, 242 SkRect* devBounds) { 243 if (pts[0].distanceToSqd(pts[1]) < kCloseSqd || pts[1].distanceToSqd(pts[2]) < kCloseSqd) { 244 if (pts[0] != pts[2]) { 245 add_line_to_segment(pts[2], segments, devBounds); 246 } 247 } else { 248 segments->push_back(); 249 segments->back().fType = Segment::kQuad; 250 segments->back().fPts[0] = pts[1]; 251 segments->back().fPts[1] = pts[2]; 252 SkASSERT(contains_inclusive(*devBounds, pts[0])); 253 devBounds->growToInclude(pts + 1, 2); 254 } 255 } 256 257 static inline void add_cubic_segments(const SkPoint pts[4], 258 SkPath::Direction dir, 259 SegmentArray* segments, 260 SkRect* devBounds) { 261 SkSTArray<15, SkPoint, true> quads; 262 GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads); 263 int count = quads.count(); 264 for (int q = 0; q < count; q += 3) { 265 add_quad_segment(&quads[q], segments, devBounds); 266 } 267 } 268 269 static bool get_segments(const SkPath& path, 270 const SkMatrix& m, 271 SegmentArray* segments, 272 SkPoint* fanPt, 273 int* vCount, 274 int* iCount, 275 SkRect* devBounds) { 276 SkPath::Iter iter(path, true); 277 // This renderer over-emphasizes very thin path regions. We use the distance 278 // to the path from the sample to compute coverage. Every pixel intersected 279 // by the path will be hit and the maximum distance is sqrt(2)/2. We don't 280 // notice that the sample may be close to a very thin area of the path and 281 // thus should be very light. This is particularly egregious for degenerate 282 // line paths. We detect paths that are very close to a line (zero area) and 283 // draw nothing. 284 DegenerateTestData degenerateData; 285 SkPath::Direction dir; 286 // get_direction can fail for some degenerate paths. 287 if (!get_direction(path, m, &dir)) { 288 return false; 289 } 290 291 for (;;) { 292 SkPoint pts[4]; 293 SkPath::Verb verb = iter.next(pts); 294 switch (verb) { 295 case SkPath::kMove_Verb: 296 m.mapPoints(pts, 1); 297 update_degenerate_test(°enerateData, pts[0]); 298 devBounds->set(pts->fX, pts->fY, pts->fX, pts->fY); 299 break; 300 case SkPath::kLine_Verb: { 301 m.mapPoints(&pts[1], 1); 302 update_degenerate_test(°enerateData, pts[1]); 303 add_line_to_segment(pts[1], segments, devBounds); 304 break; 305 } 306 case SkPath::kQuad_Verb: 307 m.mapPoints(pts, 3); 308 update_degenerate_test(°enerateData, pts[1]); 309 update_degenerate_test(°enerateData, pts[2]); 310 add_quad_segment(pts, segments, devBounds); 311 break; 312 case SkPath::kCubic_Verb: { 313 m.mapPoints(pts, 4); 314 update_degenerate_test(°enerateData, pts[1]); 315 update_degenerate_test(°enerateData, pts[2]); 316 update_degenerate_test(°enerateData, pts[3]); 317 add_cubic_segments(pts, dir, segments, devBounds); 318 break; 319 }; 320 case SkPath::kDone_Verb: 321 if (degenerateData.isDegenerate()) { 322 return false; 323 } else { 324 compute_vectors(segments, fanPt, dir, vCount, iCount); 325 return true; 326 } 327 default: 328 break; 329 } 330 } 331 } 332 333 struct QuadVertex { 334 SkPoint fPos; 335 SkPoint fUV; 336 SkScalar fD0; 337 SkScalar fD1; 338 }; 339 340 struct Draw { 341 Draw() : fVertexCnt(0), fIndexCnt(0) {} 342 int fVertexCnt; 343 int fIndexCnt; 344 }; 345 346 typedef SkTArray<Draw, true> DrawArray; 347 348 static void create_vertices(const SegmentArray& segments, 349 const SkPoint& fanPt, 350 DrawArray* draws, 351 QuadVertex* verts, 352 uint16_t* idxs) { 353 Draw* draw = &draws->push_back(); 354 // alias just to make vert/index assignments easier to read. 355 int* v = &draw->fVertexCnt; 356 int* i = &draw->fIndexCnt; 357 358 int count = segments.count(); 359 for (int a = 0; a < count; ++a) { 360 const Segment& sega = segments[a]; 361 int b = (a + 1) % count; 362 const Segment& segb = segments[b]; 363 364 // Check whether adding the verts for this segment to the current draw would cause index 365 // values to overflow. 366 int vCount = 4; 367 if (Segment::kLine == segb.fType) { 368 vCount += 5; 369 } else { 370 vCount += 6; 371 } 372 if (draw->fVertexCnt + vCount > (1 << 16)) { 373 verts += *v; 374 idxs += *i; 375 draw = &draws->push_back(); 376 v = &draw->fVertexCnt; 377 i = &draw->fIndexCnt; 378 } 379 380 // FIXME: These tris are inset in the 1 unit arc around the corner 381 verts[*v + 0].fPos = sega.endPt(); 382 verts[*v + 1].fPos = verts[*v + 0].fPos + sega.endNorm(); 383 verts[*v + 2].fPos = verts[*v + 0].fPos + segb.fMid; 384 verts[*v + 3].fPos = verts[*v + 0].fPos + segb.fNorms[0]; 385 verts[*v + 0].fUV.set(0,0); 386 verts[*v + 1].fUV.set(0,-SK_Scalar1); 387 verts[*v + 2].fUV.set(0,-SK_Scalar1); 388 verts[*v + 3].fUV.set(0,-SK_Scalar1); 389 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1; 390 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1; 391 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1; 392 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1; 393 394 idxs[*i + 0] = *v + 0; 395 idxs[*i + 1] = *v + 2; 396 idxs[*i + 2] = *v + 1; 397 idxs[*i + 3] = *v + 0; 398 idxs[*i + 4] = *v + 3; 399 idxs[*i + 5] = *v + 2; 400 401 *v += 4; 402 *i += 6; 403 404 if (Segment::kLine == segb.fType) { 405 verts[*v + 0].fPos = fanPt; 406 verts[*v + 1].fPos = sega.endPt(); 407 verts[*v + 2].fPos = segb.fPts[0]; 408 409 verts[*v + 3].fPos = verts[*v + 1].fPos + segb.fNorms[0]; 410 verts[*v + 4].fPos = verts[*v + 2].fPos + segb.fNorms[0]; 411 412 // we draw the line edge as a degenerate quad (u is 0, v is the 413 // signed distance to the edge) 414 SkScalar dist = fanPt.distanceToLineBetween(verts[*v + 1].fPos, 415 verts[*v + 2].fPos); 416 verts[*v + 0].fUV.set(0, dist); 417 verts[*v + 1].fUV.set(0, 0); 418 verts[*v + 2].fUV.set(0, 0); 419 verts[*v + 3].fUV.set(0, -SK_Scalar1); 420 verts[*v + 4].fUV.set(0, -SK_Scalar1); 421 422 verts[*v + 0].fD0 = verts[*v + 0].fD1 = -SK_Scalar1; 423 verts[*v + 1].fD0 = verts[*v + 1].fD1 = -SK_Scalar1; 424 verts[*v + 2].fD0 = verts[*v + 2].fD1 = -SK_Scalar1; 425 verts[*v + 3].fD0 = verts[*v + 3].fD1 = -SK_Scalar1; 426 verts[*v + 4].fD0 = verts[*v + 4].fD1 = -SK_Scalar1; 427 428 idxs[*i + 0] = *v + 0; 429 idxs[*i + 1] = *v + 2; 430 idxs[*i + 2] = *v + 1; 431 432 idxs[*i + 3] = *v + 3; 433 idxs[*i + 4] = *v + 1; 434 idxs[*i + 5] = *v + 2; 435 436 idxs[*i + 6] = *v + 4; 437 idxs[*i + 7] = *v + 3; 438 idxs[*i + 8] = *v + 2; 439 440 *v += 5; 441 *i += 9; 442 } else { 443 SkPoint qpts[] = {sega.endPt(), segb.fPts[0], segb.fPts[1]}; 444 445 SkVector midVec = segb.fNorms[0] + segb.fNorms[1]; 446 midVec.normalize(); 447 448 verts[*v + 0].fPos = fanPt; 449 verts[*v + 1].fPos = qpts[0]; 450 verts[*v + 2].fPos = qpts[2]; 451 verts[*v + 3].fPos = qpts[0] + segb.fNorms[0]; 452 verts[*v + 4].fPos = qpts[2] + segb.fNorms[1]; 453 verts[*v + 5].fPos = qpts[1] + midVec; 454 455 SkScalar c = segb.fNorms[0].dot(qpts[0]); 456 verts[*v + 0].fD0 = -segb.fNorms[0].dot(fanPt) + c; 457 verts[*v + 1].fD0 = 0.f; 458 verts[*v + 2].fD0 = -segb.fNorms[0].dot(qpts[2]) + c; 459 verts[*v + 3].fD0 = -SK_ScalarMax/100; 460 verts[*v + 4].fD0 = -SK_ScalarMax/100; 461 verts[*v + 5].fD0 = -SK_ScalarMax/100; 462 463 c = segb.fNorms[1].dot(qpts[2]); 464 verts[*v + 0].fD1 = -segb.fNorms[1].dot(fanPt) + c; 465 verts[*v + 1].fD1 = -segb.fNorms[1].dot(qpts[0]) + c; 466 verts[*v + 2].fD1 = 0.f; 467 verts[*v + 3].fD1 = -SK_ScalarMax/100; 468 verts[*v + 4].fD1 = -SK_ScalarMax/100; 469 verts[*v + 5].fD1 = -SK_ScalarMax/100; 470 471 GrPathUtils::QuadUVMatrix toUV(qpts); 472 toUV.apply<6, sizeof(QuadVertex), sizeof(SkPoint)>(verts + *v); 473 474 idxs[*i + 0] = *v + 3; 475 idxs[*i + 1] = *v + 1; 476 idxs[*i + 2] = *v + 2; 477 idxs[*i + 3] = *v + 4; 478 idxs[*i + 4] = *v + 3; 479 idxs[*i + 5] = *v + 2; 480 481 idxs[*i + 6] = *v + 5; 482 idxs[*i + 7] = *v + 3; 483 idxs[*i + 8] = *v + 4; 484 485 idxs[*i + 9] = *v + 0; 486 idxs[*i + 10] = *v + 2; 487 idxs[*i + 11] = *v + 1; 488 489 *v += 6; 490 *i += 12; 491 } 492 } 493 } 494 495 /////////////////////////////////////////////////////////////////////////////// 496 497 /* 498 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first 499 * two components of the vertex attribute. Coverage is based on signed 500 * distance with negative being inside, positive outside. The edge is specified in 501 * window space (y-down). If either the third or fourth component of the interpolated 502 * vertex coord is > 0 then the pixel is considered outside the edge. This is used to 503 * attempt to trim to a portion of the infinite quad. 504 * Requires shader derivative instruction support. 505 */ 506 507 class QuadEdgeEffect : public GrGeometryProcessor { 508 public: 509 510 static GrGeometryProcessor* Create() { 511 GR_CREATE_STATIC_GEOMETRY_PROCESSOR(gQuadEdgeEffect, QuadEdgeEffect, ()); 512 gQuadEdgeEffect->ref(); 513 return gQuadEdgeEffect; 514 } 515 516 virtual ~QuadEdgeEffect() {} 517 518 static const char* Name() { return "QuadEdge"; } 519 520 virtual void getConstantColorComponents(GrColor* color, 521 uint32_t* validFlags) const SK_OVERRIDE { 522 *validFlags = 0; 523 } 524 525 const GrShaderVar& inQuadEdge() const { return fInQuadEdge; } 526 527 virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE { 528 return GrTBackendGeometryProcessorFactory<QuadEdgeEffect>::getInstance(); 529 } 530 531 class GLProcessor : public GrGLGeometryProcessor { 532 public: 533 GLProcessor(const GrBackendProcessorFactory& factory, const GrProcessor&) 534 : INHERITED (factory) {} 535 536 virtual void emitCode(GrGLFullProgramBuilder* builder, 537 const GrGeometryProcessor& geometryProcessor, 538 const GrProcessorKey& key, 539 const char* outputColor, 540 const char* inputColor, 541 const TransformedCoordsArray&, 542 const TextureSamplerArray& samplers) SK_OVERRIDE { 543 const char *vsName, *fsName; 544 builder->addVarying(kVec4f_GrSLType, "QuadEdge", &vsName, &fsName); 545 546 GrGLProcessorFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder(); 547 548 SkAssertResult(fsBuilder->enableFeature( 549 GrGLFragmentShaderBuilder::kStandardDerivatives_GLSLFeature)); 550 fsBuilder->codeAppendf("\t\tfloat edgeAlpha;\n"); 551 552 // keep the derivative instructions outside the conditional 553 fsBuilder->codeAppendf("\t\tvec2 duvdx = dFdx(%s.xy);\n", fsName); 554 fsBuilder->codeAppendf("\t\tvec2 duvdy = dFdy(%s.xy);\n", fsName); 555 fsBuilder->codeAppendf("\t\tif (%s.z > 0.0 && %s.w > 0.0) {\n", fsName, fsName); 556 // today we know z and w are in device space. We could use derivatives 557 fsBuilder->codeAppendf("\t\t\tedgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);\n", fsName, 558 fsName); 559 fsBuilder->codeAppendf ("\t\t} else {\n"); 560 fsBuilder->codeAppendf("\t\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n" 561 "\t\t\t 2.0*%s.x*duvdy.x - duvdy.y);\n", 562 fsName, fsName); 563 fsBuilder->codeAppendf("\t\t\tedgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, 564 fsName); 565 fsBuilder->codeAppendf("\t\t\tedgeAlpha = " 566 "clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);\n\t\t}\n"); 567 568 569 fsBuilder->codeAppendf("\t%s = %s;\n", outputColor, 570 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("edgeAlpha")).c_str()); 571 572 const GrShaderVar& inQuadEdge = geometryProcessor.cast<QuadEdgeEffect>().inQuadEdge(); 573 GrGLVertexShaderBuilder* vsBuilder = builder->getVertexShaderBuilder(); 574 vsBuilder->codeAppendf("\t%s = %s;\n", vsName, inQuadEdge.c_str()); 575 } 576 577 static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*) {} 578 579 virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE {} 580 581 private: 582 typedef GrGLGeometryProcessor INHERITED; 583 }; 584 585 private: 586 QuadEdgeEffect() 587 : fInQuadEdge(this->addVertexAttrib(GrShaderVar("inQuadEdge", 588 kVec4f_GrSLType, 589 GrShaderVar::kAttribute_TypeModifier))) { 590 } 591 592 virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE { 593 return true; 594 } 595 596 const GrShaderVar& fInQuadEdge; 597 598 GR_DECLARE_GEOMETRY_PROCESSOR_TEST; 599 600 typedef GrFragmentProcessor INHERITED; 601 }; 602 603 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(QuadEdgeEffect); 604 605 GrGeometryProcessor* QuadEdgeEffect::TestCreate(SkRandom* random, 606 GrContext*, 607 const GrDrawTargetCaps& caps, 608 GrTexture*[]) { 609 // Doesn't work without derivative instructions. 610 return caps.shaderDerivativeSupport() ? QuadEdgeEffect::Create() : NULL; 611 } 612 613 /////////////////////////////////////////////////////////////////////////////// 614 615 bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path, 616 const SkStrokeRec& stroke, 617 const GrDrawTarget* target, 618 bool antiAlias) const { 619 return (target->caps()->shaderDerivativeSupport() && antiAlias && 620 stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex()); 621 } 622 623 namespace { 624 625 // position + edge 626 extern const GrVertexAttrib gPathAttribs[] = { 627 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, 628 {kVec4f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding} 629 }; 630 631 }; 632 633 bool GrAAConvexPathRenderer::onDrawPath(const SkPath& origPath, 634 const SkStrokeRec&, 635 GrDrawTarget* target, 636 bool antiAlias) { 637 638 const SkPath* path = &origPath; 639 if (path->isEmpty()) { 640 return true; 641 } 642 643 SkMatrix viewMatrix = target->getDrawState().getViewMatrix(); 644 GrDrawTarget::AutoStateRestore asr; 645 if (!asr.setIdentity(target, GrDrawTarget::kPreserve_ASRInit)) { 646 return false; 647 } 648 GrDrawState* drawState = target->drawState(); 649 650 // We use the fact that SkPath::transform path does subdivision based on 651 // perspective. Otherwise, we apply the view matrix when copying to the 652 // segment representation. 653 SkPath tmpPath; 654 if (viewMatrix.hasPerspective()) { 655 origPath.transform(viewMatrix, &tmpPath); 656 path = &tmpPath; 657 viewMatrix = SkMatrix::I(); 658 } 659 660 QuadVertex *verts; 661 uint16_t* idxs; 662 663 int vCount; 664 int iCount; 665 enum { 666 kPreallocSegmentCnt = 512 / sizeof(Segment), 667 kPreallocDrawCnt = 4, 668 }; 669 SkSTArray<kPreallocSegmentCnt, Segment, true> segments; 670 SkPoint fanPt; 671 672 // We can't simply use the path bounds because we may degenerate cubics to quads which produces 673 // new control points outside the original convex hull. 674 SkRect devBounds; 675 if (!get_segments(*path, viewMatrix, &segments, &fanPt, &vCount, &iCount, &devBounds)) { 676 return false; 677 } 678 679 // Our computed verts should all be within one pixel of the segment control points. 680 devBounds.outset(SK_Scalar1, SK_Scalar1); 681 682 drawState->setVertexAttribs<gPathAttribs>(SK_ARRAY_COUNT(gPathAttribs), sizeof(QuadVertex)); 683 684 GrGeometryProcessor* quadProcessor = QuadEdgeEffect::Create(); 685 drawState->setGeometryProcessor(quadProcessor)->unref(); 686 687 GrDrawTarget::AutoReleaseGeometry arg(target, vCount, iCount); 688 if (!arg.succeeded()) { 689 return false; 690 } 691 verts = reinterpret_cast<QuadVertex*>(arg.vertices()); 692 idxs = reinterpret_cast<uint16_t*>(arg.indices()); 693 694 SkSTArray<kPreallocDrawCnt, Draw, true> draws; 695 create_vertices(segments, fanPt, &draws, verts, idxs); 696 697 // Check devBounds 698 #ifdef SK_DEBUG 699 SkRect tolDevBounds = devBounds; 700 tolDevBounds.outset(SK_Scalar1 / 10000, SK_Scalar1 / 10000); 701 SkRect actualBounds; 702 actualBounds.set(verts[0].fPos, verts[1].fPos); 703 for (int i = 2; i < vCount; ++i) { 704 actualBounds.growToInclude(verts[i].fPos.fX, verts[i].fPos.fY); 705 } 706 SkASSERT(tolDevBounds.contains(actualBounds)); 707 #endif 708 709 int vOffset = 0; 710 for (int i = 0; i < draws.count(); ++i) { 711 const Draw& draw = draws[i]; 712 target->drawIndexed(kTriangles_GrPrimitiveType, 713 vOffset, // start vertex 714 0, // start index 715 draw.fVertexCnt, 716 draw.fIndexCnt, 717 &devBounds); 718 vOffset += draw.fVertexCnt; 719 } 720 721 return true; 722 } 723