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