1/* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "GrAAHairLinePathRenderer.h" 9 10#include "GrContext.h" 11#include "GrDrawState.h" 12#include "GrDrawTargetCaps.h" 13#include "GrEffect.h" 14#include "GrGpu.h" 15#include "GrIndexBuffer.h" 16#include "GrPathUtils.h" 17#include "GrTBackendEffectFactory.h" 18#include "SkGeometry.h" 19#include "SkStroke.h" 20#include "SkTemplates.h" 21 22#include "gl/GrGLEffect.h" 23#include "gl/GrGLSL.h" 24 25namespace { 26// quadratics are rendered as 5-sided polys in order to bound the 27// AA stroke around the center-curve. See comments in push_quad_index_buffer and 28// bloat_quad. Quadratics and conics share an index buffer 29static const int kVertsPerQuad = 5; 30static const int kIdxsPerQuad = 9; 31 32static const int kVertsPerLineSeg = 6; 33static const int kIdxsPerLineSeg = 12; 34 35static const int kNumQuadsInIdxBuffer = 256; 36static const size_t kQuadIdxSBufize = kIdxsPerQuad * 37 sizeof(uint16_t) * 38 kNumQuadsInIdxBuffer; 39 40static const int kNumLineSegsInIdxBuffer = 256; 41static const size_t kLineSegIdxSBufize = kIdxsPerLineSeg * 42 sizeof(uint16_t) * 43 kNumLineSegsInIdxBuffer; 44 45static bool push_quad_index_data(GrIndexBuffer* qIdxBuffer) { 46 uint16_t* data = (uint16_t*) qIdxBuffer->lock(); 47 bool tempData = NULL == data; 48 if (tempData) { 49 data = SkNEW_ARRAY(uint16_t, kNumQuadsInIdxBuffer * kIdxsPerQuad); 50 } 51 for (int i = 0; i < kNumQuadsInIdxBuffer; ++i) { 52 53 // Each quadratic is rendered as a five sided polygon. This poly bounds 54 // the quadratic's bounding triangle but has been expanded so that the 55 // 1-pixel wide area around the curve is inside the poly. 56 // If a,b,c are the original control points then the poly a0,b0,c0,c1,a1 57 // that is rendered would look like this: 58 // b0 59 // b 60 // 61 // a0 c0 62 // a c 63 // a1 c1 64 // Each is drawn as three triangles specified by these 9 indices: 65 int baseIdx = i * kIdxsPerQuad; 66 uint16_t baseVert = (uint16_t)(i * kVertsPerQuad); 67 data[0 + baseIdx] = baseVert + 0; // a0 68 data[1 + baseIdx] = baseVert + 1; // a1 69 data[2 + baseIdx] = baseVert + 2; // b0 70 data[3 + baseIdx] = baseVert + 2; // b0 71 data[4 + baseIdx] = baseVert + 4; // c1 72 data[5 + baseIdx] = baseVert + 3; // c0 73 data[6 + baseIdx] = baseVert + 1; // a1 74 data[7 + baseIdx] = baseVert + 4; // c1 75 data[8 + baseIdx] = baseVert + 2; // b0 76 } 77 if (tempData) { 78 bool ret = qIdxBuffer->updateData(data, kQuadIdxSBufize); 79 delete[] data; 80 return ret; 81 } else { 82 qIdxBuffer->unlock(); 83 return true; 84 } 85} 86 87static bool push_line_index_data(GrIndexBuffer* lIdxBuffer) { 88 uint16_t* data = (uint16_t*) lIdxBuffer->lock(); 89 bool tempData = NULL == data; 90 if (tempData) { 91 data = SkNEW_ARRAY(uint16_t, kNumLineSegsInIdxBuffer * kIdxsPerLineSeg); 92 } 93 for (int i = 0; i < kNumLineSegsInIdxBuffer; ++i) { 94 // Each line segment is rendered as two quads, with alpha = 1 along the 95 // spine of the segment, and alpha = 0 along the outer edges, represented 96 // horizontally (i.e., the line equation is t*(p1-p0) + p0) 97 // 98 // p4 p5 99 // p0 p1 100 // p2 p3 101 // 102 // Each is drawn as four triangles specified by these 12 indices: 103 int baseIdx = i * kIdxsPerLineSeg; 104 uint16_t baseVert = (uint16_t)(i * kVertsPerLineSeg); 105 data[0 + baseIdx] = baseVert + 0; // p0 106 data[1 + baseIdx] = baseVert + 1; // p1 107 data[2 + baseIdx] = baseVert + 2; // p2 108 109 data[3 + baseIdx] = baseVert + 2; // p2 110 data[4 + baseIdx] = baseVert + 1; // p1 111 data[5 + baseIdx] = baseVert + 3; // p3 112 113 data[6 + baseIdx] = baseVert + 0; // p0 114 data[7 + baseIdx] = baseVert + 5; // p5 115 data[8 + baseIdx] = baseVert + 1; // p1 116 117 data[9 + baseIdx] = baseVert + 0; // p0 118 data[10+ baseIdx] = baseVert + 4; // p4 119 data[11+ baseIdx] = baseVert + 5; // p5 120 } 121 if (tempData) { 122 bool ret = lIdxBuffer->updateData(data, kLineSegIdxSBufize); 123 delete[] data; 124 return ret; 125 } else { 126 lIdxBuffer->unlock(); 127 return true; 128 } 129} 130} 131 132GrPathRenderer* GrAAHairLinePathRenderer::Create(GrContext* context) { 133 GrGpu* gpu = context->getGpu(); 134 GrIndexBuffer* qIdxBuf = gpu->createIndexBuffer(kQuadIdxSBufize, false); 135 SkAutoTUnref<GrIndexBuffer> qIdxBuffer(qIdxBuf); 136 if (NULL == qIdxBuf || !push_quad_index_data(qIdxBuf)) { 137 return NULL; 138 } 139 GrIndexBuffer* lIdxBuf = gpu->createIndexBuffer(kLineSegIdxSBufize, false); 140 SkAutoTUnref<GrIndexBuffer> lIdxBuffer(lIdxBuf); 141 if (NULL == lIdxBuf || !push_line_index_data(lIdxBuf)) { 142 return NULL; 143 } 144 return SkNEW_ARGS(GrAAHairLinePathRenderer, 145 (context, lIdxBuf, qIdxBuf)); 146} 147 148GrAAHairLinePathRenderer::GrAAHairLinePathRenderer( 149 const GrContext* context, 150 const GrIndexBuffer* linesIndexBuffer, 151 const GrIndexBuffer* quadsIndexBuffer) { 152 fLinesIndexBuffer = linesIndexBuffer; 153 linesIndexBuffer->ref(); 154 fQuadsIndexBuffer = quadsIndexBuffer; 155 quadsIndexBuffer->ref(); 156} 157 158GrAAHairLinePathRenderer::~GrAAHairLinePathRenderer() { 159 fLinesIndexBuffer->unref(); 160 fQuadsIndexBuffer->unref(); 161} 162 163namespace { 164 165#define PREALLOC_PTARRAY(N) SkSTArray<(N),SkPoint, true> 166 167// Takes 178th time of logf on Z600 / VC2010 168int get_float_exp(float x) { 169 GR_STATIC_ASSERT(sizeof(int) == sizeof(float)); 170#if GR_DEBUG 171 static bool tested; 172 if (!tested) { 173 tested = true; 174 GrAssert(get_float_exp(0.25f) == -2); 175 GrAssert(get_float_exp(0.3f) == -2); 176 GrAssert(get_float_exp(0.5f) == -1); 177 GrAssert(get_float_exp(1.f) == 0); 178 GrAssert(get_float_exp(2.f) == 1); 179 GrAssert(get_float_exp(2.5f) == 1); 180 GrAssert(get_float_exp(8.f) == 3); 181 GrAssert(get_float_exp(100.f) == 6); 182 GrAssert(get_float_exp(1000.f) == 9); 183 GrAssert(get_float_exp(1024.f) == 10); 184 GrAssert(get_float_exp(3000000.f) == 21); 185 } 186#endif 187 const int* iptr = (const int*)&x; 188 return (((*iptr) & 0x7f800000) >> 23) - 127; 189} 190 191// Uses the max curvature function for quads to estimate 192// where to chop the conic. If the max curvature is not 193// found along the curve segment it will return 1 and 194// dst[0] is the original conic. If it returns 2 the dst[0] 195// and dst[1] are the two new conics. 196int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) { 197 SkScalar t = SkFindQuadMaxCurvature(src); 198 if (t == 0) { 199 if (dst) { 200 dst[0].set(src, weight); 201 } 202 return 1; 203 } else { 204 if (dst) { 205 SkConic conic; 206 conic.set(src, weight); 207 conic.chopAt(t, dst); 208 } 209 return 2; 210 } 211} 212 213// Calls split_conic on the entire conic and then once more on each subsection. 214// Most cases will result in either 1 conic (chop point is not within t range) 215// or 3 points (split once and then one subsection is split again). 216int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) { 217 SkConic dstTemp[2]; 218 int conicCnt = split_conic(src, dstTemp, weight); 219 if (2 == conicCnt) { 220 int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW); 221 conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW); 222 } else { 223 dst[0] = dstTemp[0]; 224 } 225 return conicCnt; 226} 227 228// returns 0 if quad/conic is degen or close to it 229// in this case approx the path with lines 230// otherwise returns 1 231int is_degen_quad_or_conic(const SkPoint p[3]) { 232 static const SkScalar gDegenerateToLineTol = SK_Scalar1; 233 static const SkScalar gDegenerateToLineTolSqd = 234 SkScalarMul(gDegenerateToLineTol, gDegenerateToLineTol); 235 236 if (p[0].distanceToSqd(p[1]) < gDegenerateToLineTolSqd || 237 p[1].distanceToSqd(p[2]) < gDegenerateToLineTolSqd) { 238 return 1; 239 } 240 241 SkScalar dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]); 242 if (dsqd < gDegenerateToLineTolSqd) { 243 return 1; 244 } 245 246 if (p[2].distanceToLineBetweenSqd(p[1], p[0]) < gDegenerateToLineTolSqd) { 247 return 1; 248 } 249 return 0; 250} 251 252// we subdivide the quads to avoid huge overfill 253// if it returns -1 then should be drawn as lines 254int num_quad_subdivs(const SkPoint p[3]) { 255 static const SkScalar gDegenerateToLineTol = SK_Scalar1; 256 static const SkScalar gDegenerateToLineTolSqd = 257 SkScalarMul(gDegenerateToLineTol, gDegenerateToLineTol); 258 259 if (p[0].distanceToSqd(p[1]) < gDegenerateToLineTolSqd || 260 p[1].distanceToSqd(p[2]) < gDegenerateToLineTolSqd) { 261 return -1; 262 } 263 264 SkScalar dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]); 265 if (dsqd < gDegenerateToLineTolSqd) { 266 return -1; 267 } 268 269 if (p[2].distanceToLineBetweenSqd(p[1], p[0]) < gDegenerateToLineTolSqd) { 270 return -1; 271 } 272 273 // tolerance of triangle height in pixels 274 // tuned on windows Quadro FX 380 / Z600 275 // trade off of fill vs cpu time on verts 276 // maybe different when do this using gpu (geo or tess shaders) 277 static const SkScalar gSubdivTol = 175 * SK_Scalar1; 278 279 if (dsqd <= SkScalarMul(gSubdivTol, gSubdivTol)) { 280 return 0; 281 } else { 282 static const int kMaxSub = 4; 283 // subdividing the quad reduces d by 4. so we want x = log4(d/tol) 284 // = log4(d*d/tol*tol)/2 285 // = log2(d*d/tol*tol) 286 287#ifdef SK_SCALAR_IS_FLOAT 288 // +1 since we're ignoring the mantissa contribution. 289 int log = get_float_exp(dsqd/(gSubdivTol*gSubdivTol)) + 1; 290 log = GrMin(GrMax(0, log), kMaxSub); 291 return log; 292#else 293 SkScalar log = SkScalarLog( 294 SkScalarDiv(dsqd, 295 SkScalarMul(gSubdivTol, gSubdivTol))); 296 static const SkScalar conv = SkScalarInvert(SkScalarLog(2)); 297 log = SkScalarMul(log, conv); 298 return GrMin(GrMax(0, SkScalarCeilToInt(log)),kMaxSub); 299#endif 300 } 301} 302 303/** 304 * Generates the lines and quads to be rendered. Lines are always recorded in 305 * device space. We will do a device space bloat to account for the 1pixel 306 * thickness. 307 * Quads are recorded in device space unless m contains 308 * perspective, then in they are in src space. We do this because we will 309 * subdivide large quads to reduce over-fill. This subdivision has to be 310 * performed before applying the perspective matrix. 311 */ 312int generate_lines_and_quads(const SkPath& path, 313 const SkMatrix& m, 314 const SkIRect& devClipBounds, 315 GrAAHairLinePathRenderer::PtArray* lines, 316 GrAAHairLinePathRenderer::PtArray* quads, 317 GrAAHairLinePathRenderer::PtArray* conics, 318 GrAAHairLinePathRenderer::IntArray* quadSubdivCnts, 319 GrAAHairLinePathRenderer::FloatArray* conicWeights) { 320 SkPath::Iter iter(path, false); 321 322 int totalQuadCount = 0; 323 SkRect bounds; 324 SkIRect ibounds; 325 326 bool persp = m.hasPerspective(); 327 328 for (;;) { 329 GrPoint pathPts[4]; 330 GrPoint devPts[4]; 331 SkPath::Verb verb = iter.next(pathPts); 332 switch (verb) { 333 case SkPath::kConic_Verb: { 334 SkConic dst[4]; 335 // We chop the conics to create tighter clipping to hide error 336 // that appears near max curvature of very thin conics. Thin 337 // hyperbolas with high weight still show error. 338 int conicCnt = chop_conic(pathPts, dst, iter.conicWeight()); 339 for (int i = 0; i < conicCnt; ++i) { 340 SkPoint* chopPnts = dst[i].fPts; 341 m.mapPoints(devPts, chopPnts, 3); 342 bounds.setBounds(devPts, 3); 343 bounds.outset(SK_Scalar1, SK_Scalar1); 344 bounds.roundOut(&ibounds); 345 if (SkIRect::Intersects(devClipBounds, ibounds)) { 346 if (is_degen_quad_or_conic(devPts)) { 347 SkPoint* pts = lines->push_back_n(4); 348 pts[0] = devPts[0]; 349 pts[1] = devPts[1]; 350 pts[2] = devPts[1]; 351 pts[3] = devPts[2]; 352 } else { 353 // when in perspective keep conics in src space 354 SkPoint* cPts = persp ? chopPnts : devPts; 355 SkPoint* pts = conics->push_back_n(3); 356 pts[0] = cPts[0]; 357 pts[1] = cPts[1]; 358 pts[2] = cPts[2]; 359 conicWeights->push_back() = dst[i].fW; 360 } 361 } 362 } 363 break; 364 } 365 case SkPath::kMove_Verb: 366 break; 367 case SkPath::kLine_Verb: 368 m.mapPoints(devPts, pathPts, 2); 369 bounds.setBounds(devPts, 2); 370 bounds.outset(SK_Scalar1, SK_Scalar1); 371 bounds.roundOut(&ibounds); 372 if (SkIRect::Intersects(devClipBounds, ibounds)) { 373 SkPoint* pts = lines->push_back_n(2); 374 pts[0] = devPts[0]; 375 pts[1] = devPts[1]; 376 } 377 break; 378 case SkPath::kQuad_Verb: { 379 SkPoint choppedPts[5]; 380 // Chopping the quad helps when the quad is either degenerate or nearly degenerate. 381 // When it is degenerate it allows the approximation with lines to work since the 382 // chop point (if there is one) will be at the parabola's vertex. In the nearly 383 // degenerate the QuadUVMatrix computed for the points is almost singular which 384 // can cause rendering artifacts. 385 int n = SkChopQuadAtMaxCurvature(pathPts, choppedPts); 386 for (int i = 0; i < n; ++i) { 387 SkPoint* quadPts = choppedPts + i * 2; 388 m.mapPoints(devPts, quadPts, 3); 389 bounds.setBounds(devPts, 3); 390 bounds.outset(SK_Scalar1, SK_Scalar1); 391 bounds.roundOut(&ibounds); 392 393 if (SkIRect::Intersects(devClipBounds, ibounds)) { 394 int subdiv = num_quad_subdivs(devPts); 395 GrAssert(subdiv >= -1); 396 if (-1 == subdiv) { 397 SkPoint* pts = lines->push_back_n(4); 398 pts[0] = devPts[0]; 399 pts[1] = devPts[1]; 400 pts[2] = devPts[1]; 401 pts[3] = devPts[2]; 402 } else { 403 // when in perspective keep quads in src space 404 SkPoint* qPts = persp ? quadPts : devPts; 405 SkPoint* pts = quads->push_back_n(3); 406 pts[0] = qPts[0]; 407 pts[1] = qPts[1]; 408 pts[2] = qPts[2]; 409 quadSubdivCnts->push_back() = subdiv; 410 totalQuadCount += 1 << subdiv; 411 } 412 } 413 } 414 break; 415 } 416 case SkPath::kCubic_Verb: 417 m.mapPoints(devPts, pathPts, 4); 418 bounds.setBounds(devPts, 4); 419 bounds.outset(SK_Scalar1, SK_Scalar1); 420 bounds.roundOut(&ibounds); 421 if (SkIRect::Intersects(devClipBounds, ibounds)) { 422 PREALLOC_PTARRAY(32) q; 423 // we don't need a direction if we aren't constraining the subdivision 424 static const SkPath::Direction kDummyDir = SkPath::kCCW_Direction; 425 // We convert cubics to quadratics (for now). 426 // In perspective have to do conversion in src space. 427 if (persp) { 428 SkScalar tolScale = 429 GrPathUtils::scaleToleranceToSrc(SK_Scalar1, m, 430 path.getBounds()); 431 GrPathUtils::convertCubicToQuads(pathPts, tolScale, false, kDummyDir, &q); 432 } else { 433 GrPathUtils::convertCubicToQuads(devPts, SK_Scalar1, false, kDummyDir, &q); 434 } 435 for (int i = 0; i < q.count(); i += 3) { 436 SkPoint* qInDevSpace; 437 // bounds has to be calculated in device space, but q is 438 // in src space when there is perspective. 439 if (persp) { 440 m.mapPoints(devPts, &q[i], 3); 441 bounds.setBounds(devPts, 3); 442 qInDevSpace = devPts; 443 } else { 444 bounds.setBounds(&q[i], 3); 445 qInDevSpace = &q[i]; 446 } 447 bounds.outset(SK_Scalar1, SK_Scalar1); 448 bounds.roundOut(&ibounds); 449 if (SkIRect::Intersects(devClipBounds, ibounds)) { 450 int subdiv = num_quad_subdivs(qInDevSpace); 451 GrAssert(subdiv >= -1); 452 if (-1 == subdiv) { 453 SkPoint* pts = lines->push_back_n(4); 454 // lines should always be in device coords 455 pts[0] = qInDevSpace[0]; 456 pts[1] = qInDevSpace[1]; 457 pts[2] = qInDevSpace[1]; 458 pts[3] = qInDevSpace[2]; 459 } else { 460 SkPoint* pts = quads->push_back_n(3); 461 // q is already in src space when there is no 462 // perspective and dev coords otherwise. 463 pts[0] = q[0 + i]; 464 pts[1] = q[1 + i]; 465 pts[2] = q[2 + i]; 466 quadSubdivCnts->push_back() = subdiv; 467 totalQuadCount += 1 << subdiv; 468 } 469 } 470 } 471 } 472 break; 473 case SkPath::kClose_Verb: 474 break; 475 case SkPath::kDone_Verb: 476 return totalQuadCount; 477 } 478 } 479} 480 481struct LineVertex { 482 GrPoint fPos; 483 GrColor fCoverage; 484}; 485 486struct BezierVertex { 487 GrPoint fPos; 488 union { 489 struct { 490 SkScalar fK; 491 SkScalar fL; 492 SkScalar fM; 493 } fConic; 494 GrVec fQuadCoord; 495 struct { 496 SkScalar fBogus[4]; 497 }; 498 }; 499}; 500 501GR_STATIC_ASSERT(sizeof(BezierVertex) == 3 * sizeof(GrPoint)); 502 503void intersect_lines(const SkPoint& ptA, const SkVector& normA, 504 const SkPoint& ptB, const SkVector& normB, 505 SkPoint* result) { 506 507 SkScalar lineAW = -normA.dot(ptA); 508 SkScalar lineBW = -normB.dot(ptB); 509 510 SkScalar wInv = SkScalarMul(normA.fX, normB.fY) - 511 SkScalarMul(normA.fY, normB.fX); 512 wInv = SkScalarInvert(wInv); 513 514 result->fX = SkScalarMul(normA.fY, lineBW) - SkScalarMul(lineAW, normB.fY); 515 result->fX = SkScalarMul(result->fX, wInv); 516 517 result->fY = SkScalarMul(lineAW, normB.fX) - SkScalarMul(normA.fX, lineBW); 518 result->fY = SkScalarMul(result->fY, wInv); 519} 520 521void set_uv_quad(const SkPoint qpts[3], BezierVertex verts[kVertsPerQuad]) { 522 // this should be in the src space, not dev coords, when we have perspective 523 GrPathUtils::QuadUVMatrix DevToUV(qpts); 524 DevToUV.apply<kVertsPerQuad, sizeof(BezierVertex), sizeof(GrPoint)>(verts); 525} 526 527void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice, 528 const SkMatrix* toSrc, BezierVertex verts[kVertsPerQuad], 529 SkRect* devBounds) { 530 GrAssert(!toDevice == !toSrc); 531 // original quad is specified by tri a,b,c 532 SkPoint a = qpts[0]; 533 SkPoint b = qpts[1]; 534 SkPoint c = qpts[2]; 535 536 if (toDevice) { 537 toDevice->mapPoints(&a, 1); 538 toDevice->mapPoints(&b, 1); 539 toDevice->mapPoints(&c, 1); 540 } 541 // make a new poly where we replace a and c by a 1-pixel wide edges orthog 542 // to edges ab and bc: 543 // 544 // before | after 545 // | b0 546 // b | 547 // | 548 // | a0 c0 549 // a c | a1 c1 550 // 551 // edges a0->b0 and b0->c0 are parallel to original edges a->b and b->c, 552 // respectively. 553 BezierVertex& a0 = verts[0]; 554 BezierVertex& a1 = verts[1]; 555 BezierVertex& b0 = verts[2]; 556 BezierVertex& c0 = verts[3]; 557 BezierVertex& c1 = verts[4]; 558 559 SkVector ab = b; 560 ab -= a; 561 SkVector ac = c; 562 ac -= a; 563 SkVector cb = b; 564 cb -= c; 565 566 // We should have already handled degenerates 567 GrAssert(ab.length() > 0 && cb.length() > 0); 568 569 ab.normalize(); 570 SkVector abN; 571 abN.setOrthog(ab, SkVector::kLeft_Side); 572 if (abN.dot(ac) > 0) { 573 abN.negate(); 574 } 575 576 cb.normalize(); 577 SkVector cbN; 578 cbN.setOrthog(cb, SkVector::kLeft_Side); 579 if (cbN.dot(ac) < 0) { 580 cbN.negate(); 581 } 582 583 a0.fPos = a; 584 a0.fPos += abN; 585 a1.fPos = a; 586 a1.fPos -= abN; 587 588 c0.fPos = c; 589 c0.fPos += cbN; 590 c1.fPos = c; 591 c1.fPos -= cbN; 592 593 // This point may not be within 1 pixel of a control point. We update the bounding box to 594 // include it. 595 intersect_lines(a0.fPos, abN, c0.fPos, cbN, &b0.fPos); 596 devBounds->growToInclude(b0.fPos.fX, b0.fPos.fY); 597 598 if (toSrc) { 599 toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(BezierVertex), kVertsPerQuad); 600 } 601} 602 603// Input: 604// Three control points: p[0], p[1], p[2] and weight: w 605// Output: 606// Let: 607// l = (2*w * (y1 - y0), 2*w * (x0 - x1), 2*w * (x1*y0 - x0*y1)) 608// m = (2*w * (y2 - y1), 2*w * (x1 - x2), 2*w * (x2*y1 - x1*y2)) 609// k = (y2 - y0, x0 - x2, (x2 - x0)*y0 - (y2 - y0)*x0 ) 610void calc_conic_klm(const SkPoint p[3], const SkScalar weight, 611 SkScalar k[3], SkScalar l[3], SkScalar m[3]) { 612 const SkScalar w2 = 2 * weight; 613 l[0] = w2 * (p[1].fY - p[0].fY); 614 l[1] = w2 * (p[0].fX - p[1].fX); 615 l[2] = w2 * (p[1].fX * p[0].fY - p[0].fX * p[1].fY); 616 617 m[0] = w2 * (p[2].fY - p[1].fY); 618 m[1] = w2 * (p[1].fX - p[2].fX); 619 m[2] = w2 * (p[2].fX * p[1].fY - p[1].fX * p[2].fY); 620 621 k[0] = p[2].fY - p[0].fY; 622 k[1] = p[0].fX - p[2].fX; 623 k[2] = (p[2].fX - p[0].fX) * p[0].fY - (p[2].fY - p[0].fY) * p[0].fX; 624 625 // scale the max absolute value of coeffs to 10 626 SkScalar scale = 0.0f; 627 for (int i = 0; i < 3; ++i) { 628 scale = SkMaxScalar(scale, SkScalarAbs(k[i])); 629 scale = SkMaxScalar(scale, SkScalarAbs(l[i])); 630 scale = SkMaxScalar(scale, SkScalarAbs(m[i])); 631 } 632 GrAssert(scale > 0); 633 scale /= 10.0f; 634 k[0] /= scale; 635 k[1] /= scale; 636 k[2] /= scale; 637 l[0] /= scale; 638 l[1] /= scale; 639 l[2] /= scale; 640 m[0] /= scale; 641 m[1] /= scale; 642 m[2] /= scale; 643} 644 645// Equations based off of Loop-Blinn Quadratic GPU Rendering 646// Input Parametric: 647// P(t) = (P0*(1-t)^2 + 2*w*P1*t*(1-t) + P2*t^2) / (1-t)^2 + 2*w*t*(1-t) + t^2) 648// Output Implicit: 649// f(x, y, w) = f(P) = K^2 - LM 650// K = dot(k, P), L = dot(l, P), M = dot(m, P) 651// k, l, m are calculated in function calc_conic_klm 652void set_conic_coeffs(const SkPoint p[3], BezierVertex verts[kVertsPerQuad], const float weight) { 653 SkScalar k[3]; 654 SkScalar l[3]; 655 SkScalar m[3]; 656 657 calc_conic_klm(p, weight, k, l, m); 658 659 for (int i = 0; i < kVertsPerQuad; ++i) { 660 const SkPoint pnt = verts[i].fPos; 661 verts[i].fConic.fK = pnt.fX * k[0] + pnt.fY * k[1] + k[2]; 662 verts[i].fConic.fL = pnt.fX * l[0] + pnt.fY * l[1] + l[2]; 663 verts[i].fConic.fM = pnt.fX * m[0] + pnt.fY * m[1] + m[2]; 664 } 665} 666 667void add_conics(const SkPoint p[3], 668 float weight, 669 const SkMatrix* toDevice, 670 const SkMatrix* toSrc, 671 BezierVertex** vert, 672 SkRect* devBounds) { 673 bloat_quad(p, toDevice, toSrc, *vert, devBounds); 674 set_conic_coeffs(p, *vert, weight); 675 *vert += kVertsPerQuad; 676} 677 678void add_quads(const SkPoint p[3], 679 int subdiv, 680 const SkMatrix* toDevice, 681 const SkMatrix* toSrc, 682 BezierVertex** vert, 683 SkRect* devBounds) { 684 GrAssert(subdiv >= 0); 685 if (subdiv) { 686 SkPoint newP[5]; 687 SkChopQuadAtHalf(p, newP); 688 add_quads(newP + 0, subdiv-1, toDevice, toSrc, vert, devBounds); 689 add_quads(newP + 2, subdiv-1, toDevice, toSrc, vert, devBounds); 690 } else { 691 bloat_quad(p, toDevice, toSrc, *vert, devBounds); 692 set_uv_quad(p, *vert); 693 *vert += kVertsPerQuad; 694 } 695} 696 697void add_line(const SkPoint p[2], 698 int rtHeight, 699 const SkMatrix* toSrc, 700 GrColor coverage, 701 LineVertex** vert) { 702 const SkPoint& a = p[0]; 703 const SkPoint& b = p[1]; 704 705 SkVector orthVec = b; 706 orthVec -= a; 707 708 if (orthVec.setLength(SK_Scalar1)) { 709 orthVec.setOrthog(orthVec); 710 711 for (int i = 0; i < kVertsPerLineSeg; ++i) { 712 (*vert)[i].fPos = (i & 0x1) ? b : a; 713 if (i & 0x2) { 714 (*vert)[i].fPos += orthVec; 715 (*vert)[i].fCoverage = 0; 716 } else if (i & 0x4) { 717 (*vert)[i].fPos -= orthVec; 718 (*vert)[i].fCoverage = 0; 719 } else { 720 (*vert)[i].fCoverage = coverage; 721 } 722 } 723 if (NULL != toSrc) { 724 toSrc->mapPointsWithStride(&(*vert)->fPos, 725 sizeof(LineVertex), 726 kVertsPerLineSeg); 727 } 728 } else { 729 // just make it degenerate and likely offscreen 730 for (int i = 0; i < kVertsPerLineSeg; ++i) { 731 (*vert)[i].fPos.set(SK_ScalarMax, SK_ScalarMax); 732 } 733 } 734 735 *vert += kVertsPerLineSeg; 736} 737 738} 739 740/** 741 * Shader is based off of Loop-Blinn Quadratic GPU Rendering 742 * The output of this effect is a hairline edge for conics. 743 * Conics specified by implicit equation K^2 - LM. 744 * K, L, and M, are the first three values of the vertex attribute, 745 * the fourth value is not used. Distance is calculated using a 746 * first order approximation from the taylor series. 747 * Coverage is max(0, 1-distance). 748 */ 749 750/** 751 * Test were also run using a second order distance approximation. 752 * There were two versions of the second order approx. The first version 753 * is of roughly the form: 754 * f(q) = |f(p)| - ||f'(p)||*||q-p|| - ||f''(p)||*||q-p||^2. 755 * The second is similar: 756 * f(q) = |f(p)| + ||f'(p)||*||q-p|| + ||f''(p)||*||q-p||^2. 757 * The exact version of the equations can be found in the paper 758 * "Distance Approximations for Rasterizing Implicit Curves" by Gabriel Taubin 759 * 760 * In both versions we solve the quadratic for ||q-p||. 761 * Version 1: 762 * gFM is magnitude of first partials and gFM2 is magnitude of 2nd partials (as derived from paper) 763 * builder->fsCodeAppend("\t\tedgeAlpha = (sqrt(gFM*gFM+4.0*func*gF2M) - gFM)/(2.0*gF2M);\n"); 764 * Version 2: 765 * builder->fsCodeAppend("\t\tedgeAlpha = (gFM - sqrt(gFM*gFM-4.0*func*gF2M))/(2.0*gF2M);\n"); 766 * 767 * Also note that 2nd partials of k,l,m are zero 768 * 769 * When comparing the two second order approximations to the first order approximations, 770 * the following results were found. Version 1 tends to underestimate the distances, thus it 771 * basically increases all the error that we were already seeing in the first order 772 * approx. So this version is not the one to use. Version 2 has the opposite effect 773 * and tends to overestimate the distances. This is much closer to what we are 774 * looking for. It is able to render ellipses (even thin ones) without the need to chop. 775 * However, it can not handle thin hyperbolas well and thus would still rely on 776 * chopping to tighten the clipping. Another side effect of the overestimating is 777 * that the curves become much thinner and "ropey". If all that was ever rendered 778 * were "not too thin" curves and ellipses then 2nd order may have an advantage since 779 * only one geometry would need to be rendered. However no benches were run comparing 780 * chopped first order and non chopped 2nd order. 781 */ 782class HairConicEdgeEffect : public GrEffect { 783public: 784 static GrEffectRef* Create() { 785 GR_CREATE_STATIC_EFFECT(gHairConicEdgeEffect, HairConicEdgeEffect, ()); 786 gHairConicEdgeEffect->ref(); 787 return gHairConicEdgeEffect; 788 } 789 790 virtual ~HairConicEdgeEffect() {} 791 792 static const char* Name() { return "HairConicEdge"; } 793 794 virtual void getConstantColorComponents(GrColor* color, 795 uint32_t* validFlags) const SK_OVERRIDE { 796 *validFlags = 0; 797 } 798 799 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 800 return GrTBackendEffectFactory<HairConicEdgeEffect>::getInstance(); 801 } 802 803 class GLEffect : public GrGLEffect { 804 public: 805 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) 806 : INHERITED (factory) {} 807 808 virtual void emitCode(GrGLShaderBuilder* builder, 809 const GrDrawEffect& drawEffect, 810 EffectKey key, 811 const char* outputColor, 812 const char* inputColor, 813 const TextureSamplerArray& samplers) SK_OVERRIDE { 814 const char *vsName, *fsName; 815 816 SkAssertResult(builder->enableFeature( 817 GrGLShaderBuilder::kStandardDerivatives_GLSLFeature)); 818 builder->addVarying(kVec4f_GrSLType, "ConicCoeffs", 819 &vsName, &fsName); 820 const SkString* attr0Name = 821 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); 822 builder->vsCodeAppendf("\t%s = %s;\n", vsName, attr0Name->c_str()); 823 824 builder->fsCodeAppend("\t\tfloat edgeAlpha;\n"); 825 826 builder->fsCodeAppendf("\t\tvec3 dklmdx = dFdx(%s.xyz);\n", fsName); 827 builder->fsCodeAppendf("\t\tvec3 dklmdy = dFdy(%s.xyz);\n", fsName); 828 builder->fsCodeAppendf("\t\tfloat dfdx =\n" 829 "\t\t\t2.0*%s.x*dklmdx.x - %s.y*dklmdx.z - %s.z*dklmdx.y;\n", 830 fsName, fsName, fsName); 831 builder->fsCodeAppendf("\t\tfloat dfdy =\n" 832 "\t\t\t2.0*%s.x*dklmdy.x - %s.y*dklmdy.z - %s.z*dklmdy.y;\n", 833 fsName, fsName, fsName); 834 builder->fsCodeAppend("\t\tvec2 gF = vec2(dfdx, dfdy);\n"); 835 builder->fsCodeAppend("\t\tfloat gFM = sqrt(dot(gF, gF));\n"); 836 builder->fsCodeAppendf("\t\tfloat func = abs(%s.x*%s.x - %s.y*%s.z);\n", fsName, fsName, 837 fsName, fsName); 838 builder->fsCodeAppend("\t\tedgeAlpha = func / gFM;\n"); 839 builder->fsCodeAppend("\t\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n"); 840 // Add line below for smooth cubic ramp 841 // builder->fsCodeAppend("\t\tedgeAlpha = edgeAlpha*edgeAlpha*(3.0-2.0*edgeAlpha);\n"); 842 843 SkString modulate; 844 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha"); 845 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str()); 846 } 847 848 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { 849 return 0x0; 850 } 851 852 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {} 853 854 private: 855 typedef GrGLEffect INHERITED; 856 }; 857 858private: 859 HairConicEdgeEffect() { 860 this->addVertexAttrib(kVec4f_GrSLType); 861 } 862 863 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { 864 return true; 865 } 866 867 GR_DECLARE_EFFECT_TEST; 868 869 typedef GrEffect INHERITED; 870}; 871 872GR_DEFINE_EFFECT_TEST(HairConicEdgeEffect); 873 874GrEffectRef* HairConicEdgeEffect::TestCreate(SkMWCRandom* random, 875 GrContext*, 876 const GrDrawTargetCaps& caps, 877 GrTexture*[]) { 878 return caps.shaderDerivativeSupport() ? HairConicEdgeEffect::Create() : NULL; 879} 880 881/** 882 * The output of this effect is a hairline edge for quadratics. 883 * Quadratic specified by 0=u^2-v canonical coords. u and v are the first 884 * two components of the vertex attribute. Uses unsigned distance. 885 * Coverage is min(0, 1-distance). 3rd & 4th component unused. 886 * Requires shader derivative instruction support. 887 */ 888class HairQuadEdgeEffect : public GrEffect { 889public: 890 891 static GrEffectRef* Create() { 892 GR_CREATE_STATIC_EFFECT(gHairQuadEdgeEffect, HairQuadEdgeEffect, ()); 893 gHairQuadEdgeEffect->ref(); 894 return gHairQuadEdgeEffect; 895 } 896 897 virtual ~HairQuadEdgeEffect() {} 898 899 static const char* Name() { return "HairQuadEdge"; } 900 901 virtual void getConstantColorComponents(GrColor* color, 902 uint32_t* validFlags) const SK_OVERRIDE { 903 *validFlags = 0; 904 } 905 906 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 907 return GrTBackendEffectFactory<HairQuadEdgeEffect>::getInstance(); 908 } 909 910 class GLEffect : public GrGLEffect { 911 public: 912 GLEffect(const GrBackendEffectFactory& factory, const GrDrawEffect&) 913 : INHERITED (factory) {} 914 915 virtual void emitCode(GrGLShaderBuilder* builder, 916 const GrDrawEffect& drawEffect, 917 EffectKey key, 918 const char* outputColor, 919 const char* inputColor, 920 const TextureSamplerArray& samplers) SK_OVERRIDE { 921 const char *vsName, *fsName; 922 const SkString* attrName = 923 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); 924 builder->fsCodeAppendf("\t\tfloat edgeAlpha;\n"); 925 926 SkAssertResult(builder->enableFeature( 927 GrGLShaderBuilder::kStandardDerivatives_GLSLFeature)); 928 builder->addVarying(kVec4f_GrSLType, "HairQuadEdge", &vsName, &fsName); 929 930 builder->fsCodeAppendf("\t\tvec2 duvdx = dFdx(%s.xy);\n", fsName); 931 builder->fsCodeAppendf("\t\tvec2 duvdy = dFdy(%s.xy);\n", fsName); 932 builder->fsCodeAppendf("\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n" 933 "\t\t 2.0*%s.x*duvdy.x - duvdy.y);\n", 934 fsName, fsName); 935 builder->fsCodeAppendf("\t\tedgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, 936 fsName); 937 builder->fsCodeAppend("\t\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / dot(gF, gF));\n"); 938 builder->fsCodeAppend("\t\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n"); 939 940 SkString modulate; 941 GrGLSLModulatef<4>(&modulate, inputColor, "edgeAlpha"); 942 builder->fsCodeAppendf("\t%s = %s;\n", outputColor, modulate.c_str()); 943 944 builder->vsCodeAppendf("\t%s = %s;\n", vsName, attrName->c_str()); 945 } 946 947 static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { 948 return 0x0; 949 } 950 951 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE {} 952 953 private: 954 typedef GrGLEffect INHERITED; 955 }; 956 957private: 958 HairQuadEdgeEffect() { 959 this->addVertexAttrib(kVec4f_GrSLType); 960 } 961 962 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE { 963 return true; 964 } 965 966 GR_DECLARE_EFFECT_TEST; 967 968 typedef GrEffect INHERITED; 969}; 970 971GR_DEFINE_EFFECT_TEST(HairQuadEdgeEffect); 972 973GrEffectRef* HairQuadEdgeEffect::TestCreate(SkMWCRandom* random, 974 GrContext*, 975 const GrDrawTargetCaps& caps, 976 GrTexture*[]) { 977 // Doesn't work without derivative instructions. 978 return caps.shaderDerivativeSupport() ? HairQuadEdgeEffect::Create() : NULL; 979} 980 981/////////////////////////////////////////////////////////////////////////////// 982 983namespace { 984 985// position + edge 986extern const GrVertexAttrib gHairlineBezierAttribs[] = { 987 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, 988 {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding} 989}; 990 991// position + coverage 992extern const GrVertexAttrib gHairlineLineAttribs[] = { 993 {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}, 994 {kVec4ub_GrVertexAttribType, sizeof(GrPoint), kCoverage_GrVertexAttribBinding}, 995}; 996 997}; 998 999bool GrAAHairLinePathRenderer::createLineGeom( 1000 const SkPath& path, 1001 GrDrawTarget* target, 1002 const PtArray& lines, 1003 int lineCnt, 1004 GrDrawTarget::AutoReleaseGeometry* arg, 1005 SkRect* devBounds) { 1006 GrDrawState* drawState = target->drawState(); 1007 int rtHeight = drawState->getRenderTarget()->height(); 1008 1009 const SkMatrix& viewM = drawState->getViewMatrix(); 1010 1011 *devBounds = path.getBounds(); 1012 viewM.mapRect(devBounds); 1013 devBounds->outset(SK_Scalar1, SK_Scalar1); 1014 1015 int vertCnt = kVertsPerLineSeg * lineCnt; 1016 1017 target->drawState()->setVertexAttribs<gHairlineLineAttribs>(SK_ARRAY_COUNT(gHairlineLineAttribs)); 1018 GrAssert(sizeof(LineVertex) == target->getDrawState().getVertexSize()); 1019 1020 if (!arg->set(target, vertCnt, 0)) { 1021 return false; 1022 } 1023 1024 LineVertex* verts = reinterpret_cast<LineVertex*>(arg->vertices()); 1025 1026 const SkMatrix* toSrc = NULL; 1027 SkMatrix ivm; 1028 1029 if (viewM.hasPerspective()) { 1030 if (viewM.invert(&ivm)) { 1031 toSrc = &ivm; 1032 } 1033 } 1034 1035 for (int i = 0; i < lineCnt; ++i) { 1036 add_line(&lines[2*i], rtHeight, toSrc, drawState->getCoverage(), &verts); 1037 } 1038 1039 return true; 1040} 1041 1042bool GrAAHairLinePathRenderer::createBezierGeom( 1043 const SkPath& path, 1044 GrDrawTarget* target, 1045 const PtArray& quads, 1046 int quadCnt, 1047 const PtArray& conics, 1048 int conicCnt, 1049 const IntArray& qSubdivs, 1050 const FloatArray& cWeights, 1051 GrDrawTarget::AutoReleaseGeometry* arg, 1052 SkRect* devBounds) { 1053 GrDrawState* drawState = target->drawState(); 1054 1055 const SkMatrix& viewM = drawState->getViewMatrix(); 1056 1057 // All the vertices that we compute are within 1 of path control points with the exception of 1058 // one of the bounding vertices for each quad. The add_quads() function will update the bounds 1059 // for each quad added. 1060 *devBounds = path.getBounds(); 1061 viewM.mapRect(devBounds); 1062 devBounds->outset(SK_Scalar1, SK_Scalar1); 1063 1064 int vertCnt = kVertsPerQuad * quadCnt + kVertsPerQuad * conicCnt; 1065 1066 target->drawState()->setVertexAttribs<gHairlineBezierAttribs>(SK_ARRAY_COUNT(gHairlineBezierAttribs)); 1067 GrAssert(sizeof(BezierVertex) == target->getDrawState().getVertexSize()); 1068 1069 if (!arg->set(target, vertCnt, 0)) { 1070 return false; 1071 } 1072 1073 BezierVertex* verts = reinterpret_cast<BezierVertex*>(arg->vertices()); 1074 1075 const SkMatrix* toDevice = NULL; 1076 const SkMatrix* toSrc = NULL; 1077 SkMatrix ivm; 1078 1079 if (viewM.hasPerspective()) { 1080 if (viewM.invert(&ivm)) { 1081 toDevice = &viewM; 1082 toSrc = &ivm; 1083 } 1084 } 1085 1086 int unsubdivQuadCnt = quads.count() / 3; 1087 for (int i = 0; i < unsubdivQuadCnt; ++i) { 1088 GrAssert(qSubdivs[i] >= 0); 1089 add_quads(&quads[3*i], qSubdivs[i], toDevice, toSrc, &verts, devBounds); 1090 } 1091 1092 // Start Conics 1093 for (int i = 0; i < conicCnt; ++i) { 1094 add_conics(&conics[3*i], cWeights[i], toDevice, toSrc, &verts, devBounds); 1095 } 1096 return true; 1097} 1098 1099bool GrAAHairLinePathRenderer::canDrawPath(const SkPath& path, 1100 const SkStrokeRec& stroke, 1101 const GrDrawTarget* target, 1102 bool antiAlias) const { 1103 if (!stroke.isHairlineStyle() || !antiAlias) { 1104 return false; 1105 } 1106 1107 if (SkPath::kLine_SegmentMask == path.getSegmentMasks() || 1108 target->caps()->shaderDerivativeSupport()) { 1109 return true; 1110 } 1111 return false; 1112} 1113 1114template <class VertexType> 1115bool check_bounds(GrDrawState* drawState, const SkRect& devBounds, void* vertices, int vCount) 1116{ 1117 SkRect tolDevBounds = devBounds; 1118 tolDevBounds.outset(SK_Scalar1 / 10000, SK_Scalar1 / 10000); 1119 SkRect actualBounds; 1120 1121 VertexType* verts = reinterpret_cast<VertexType*>(vertices); 1122 bool first = true; 1123 for (int i = 0; i < vCount; ++i) { 1124 SkPoint pos = verts[i].fPos; 1125 // This is a hack to workaround the fact that we move some degenerate segments offscreen. 1126 if (SK_ScalarMax == pos.fX) { 1127 continue; 1128 } 1129 drawState->getViewMatrix().mapPoints(&pos, 1); 1130 if (first) { 1131 actualBounds.set(pos.fX, pos.fY, pos.fX, pos.fY); 1132 first = false; 1133 } else { 1134 actualBounds.growToInclude(pos.fX, pos.fY); 1135 } 1136 } 1137 if (!first) { 1138 return tolDevBounds.contains(actualBounds); 1139 } 1140 1141 return true; 1142} 1143 1144bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path, 1145 const SkStrokeRec&, 1146 GrDrawTarget* target, 1147 bool antiAlias) { 1148 1149 GrDrawState* drawState = target->drawState(); 1150 1151 SkIRect devClipBounds; 1152 target->getClip()->getConservativeBounds(drawState->getRenderTarget(), &devClipBounds); 1153 1154 int lineCnt; 1155 int quadCnt; 1156 int conicCnt; 1157 PREALLOC_PTARRAY(128) lines; 1158 PREALLOC_PTARRAY(128) quads; 1159 PREALLOC_PTARRAY(128) conics; 1160 IntArray qSubdivs; 1161 FloatArray cWeights; 1162 quadCnt = generate_lines_and_quads(path, drawState->getViewMatrix(), devClipBounds, 1163 &lines, &quads, &conics, &qSubdivs, &cWeights); 1164 lineCnt = lines.count() / 2; 1165 conicCnt = conics.count() / 3; 1166 1167 // do lines first 1168 { 1169 GrDrawTarget::AutoReleaseGeometry arg; 1170 SkRect devBounds; 1171 1172 if (!this->createLineGeom(path, 1173 target, 1174 lines, 1175 lineCnt, 1176 &arg, 1177 &devBounds)) { 1178 return false; 1179 } 1180 1181 GrDrawTarget::AutoStateRestore asr; 1182 1183 // createGeom transforms the geometry to device space when the matrix does not have 1184 // perspective. 1185 if (target->getDrawState().getViewMatrix().hasPerspective()) { 1186 asr.set(target, GrDrawTarget::kPreserve_ASRInit); 1187 } else if (!asr.setIdentity(target, GrDrawTarget::kPreserve_ASRInit)) { 1188 return false; 1189 } 1190 GrDrawState* drawState = target->drawState(); 1191 1192 // Check devBounds 1193 SkASSERT(check_bounds<LineVertex>(drawState, devBounds, arg.vertices(), 1194 kVertsPerLineSeg * lineCnt)); 1195 1196 { 1197 GrDrawState::AutoRestoreEffects are(drawState); 1198 target->setIndexSourceToBuffer(fLinesIndexBuffer); 1199 int lines = 0; 1200 while (lines < lineCnt) { 1201 int n = GrMin(lineCnt - lines, kNumLineSegsInIdxBuffer); 1202 target->drawIndexed(kTriangles_GrPrimitiveType, 1203 kVertsPerLineSeg*lines, // startV 1204 0, // startI 1205 kVertsPerLineSeg*n, // vCount 1206 kIdxsPerLineSeg*n, 1207 &devBounds); // iCount 1208 lines += n; 1209 } 1210 } 1211 } 1212 1213 // then quadratics/conics 1214 { 1215 GrDrawTarget::AutoReleaseGeometry arg; 1216 SkRect devBounds; 1217 1218 if (!this->createBezierGeom(path, 1219 target, 1220 quads, 1221 quadCnt, 1222 conics, 1223 conicCnt, 1224 qSubdivs, 1225 cWeights, 1226 &arg, 1227 &devBounds)) { 1228 return false; 1229 } 1230 1231 GrDrawTarget::AutoStateRestore asr; 1232 1233 // createGeom transforms the geometry to device space when the matrix does not have 1234 // perspective. 1235 if (target->getDrawState().getViewMatrix().hasPerspective()) { 1236 asr.set(target, GrDrawTarget::kPreserve_ASRInit); 1237 } else if (!asr.setIdentity(target, GrDrawTarget::kPreserve_ASRInit)) { 1238 return false; 1239 } 1240 GrDrawState* drawState = target->drawState(); 1241 1242 static const int kEdgeAttrIndex = 1; 1243 1244 GrEffectRef* hairQuadEffect = HairQuadEdgeEffect::Create(); 1245 GrEffectRef* hairConicEffect = HairConicEdgeEffect::Create(); 1246 1247 // Check devBounds 1248 SkASSERT(check_bounds<BezierVertex>(drawState, devBounds, arg.vertices(), 1249 kVertsPerQuad * quadCnt + kVertsPerQuad * conicCnt)); 1250 1251 { 1252 GrDrawState::AutoRestoreEffects are(drawState); 1253 target->setIndexSourceToBuffer(fQuadsIndexBuffer); 1254 int quads = 0; 1255 drawState->addCoverageEffect(hairQuadEffect, kEdgeAttrIndex)->unref(); 1256 while (quads < quadCnt) { 1257 int n = GrMin(quadCnt - quads, kNumQuadsInIdxBuffer); 1258 target->drawIndexed(kTriangles_GrPrimitiveType, 1259 kVertsPerQuad*quads, // startV 1260 0, // startI 1261 kVertsPerQuad*n, // vCount 1262 kIdxsPerQuad*n, // iCount 1263 &devBounds); 1264 quads += n; 1265 } 1266 } 1267 1268 { 1269 GrDrawState::AutoRestoreEffects are(drawState); 1270 int conics = 0; 1271 drawState->addCoverageEffect(hairConicEffect, 1, 2)->unref(); 1272 while (conics < conicCnt) { 1273 int n = GrMin(conicCnt - conics, kNumQuadsInIdxBuffer); 1274 target->drawIndexed(kTriangles_GrPrimitiveType, 1275 kVertsPerQuad*(quadCnt + conics), // startV 1276 0, // startI 1277 kVertsPerQuad*n, // vCount 1278 kIdxsPerQuad*n, // iCount 1279 &devBounds); 1280 conics += n; 1281 } 1282 } 1283 } 1284 1285 target->resetIndexSource(); 1286 1287 return true; 1288} 1289