beziereffects.cpp revision 385fe4d4b62d7d1dd76116dd570df3290a2f487b
1 2/* 3 * Copyright 2013 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// This test only works with the GPU backend. 10 11#include "gm.h" 12 13#if SK_SUPPORT_GPU 14 15#include "GrBatchFlushState.h" 16#include "GrContext.h" 17#include "GrPathUtils.h" 18#include "GrTest.h" 19#include "SkColorPriv.h" 20#include "SkDevice.h" 21#include "SkGeometry.h" 22 23#include "batches/GrTestBatch.h" 24 25#include "effects/GrBezierEffect.h" 26 27static inline SkScalar eval_line(const SkPoint& p, const SkScalar lineEq[3], SkScalar sign) { 28 return sign * (lineEq[0] * p.fX + lineEq[1] * p.fY + lineEq[2]); 29} 30 31namespace skiagm { 32 33class BezierCubicOrConicTestBatch : public GrTestBatch { 34public: 35 struct Geometry : public GrTestBatch::Geometry { 36 SkRect fBounds; 37 }; 38 39 const char* name() const override { return "BezierCubicOrConicTestBatch"; } 40 41 static GrDrawBatch* Create(const GrGeometryProcessor* gp, const Geometry& geo, 42 const SkScalar klmEqs[9], SkScalar sign) { 43 return new BezierCubicOrConicTestBatch(gp, geo, klmEqs, sign); 44 } 45 46private: 47 BezierCubicOrConicTestBatch(const GrGeometryProcessor* gp, const Geometry& geo, 48 const SkScalar klmEqs[9], SkScalar sign) 49 : INHERITED(gp, geo.fBounds) { 50 this->initClassID<BezierCubicOrConicTestBatch>(); 51 for (int i = 0; i < 9; i++) { 52 fKlmEqs[i] = klmEqs[i]; 53 } 54 55 fGeometry = geo; 56 fSign = sign; 57 } 58 59 struct Vertex { 60 SkPoint fPosition; 61 float fKLM[4]; // The last value is ignored. The effect expects a vec4f. 62 }; 63 64 Geometry* geoData(int index) override { 65 SkASSERT(0 == index); 66 return &fGeometry; 67 } 68 69 const Geometry* geoData(int index) const override { 70 SkASSERT(0 == index); 71 return &fGeometry; 72 } 73 74 void generateGeometry(Target* target) override { 75 QuadHelper helper; 76 size_t vertexStride = this->geometryProcessor()->getVertexStride(); 77 SkASSERT(vertexStride == sizeof(Vertex)); 78 Vertex* verts = reinterpret_cast<Vertex*>(helper.init(target, vertexStride, 1)); 79 if (!verts) { 80 return; 81 } 82 83 verts[0].fPosition.setRectFan(fGeometry.fBounds.fLeft, fGeometry.fBounds.fTop, 84 fGeometry.fBounds.fRight, fGeometry.fBounds.fBottom, 85 sizeof(Vertex)); 86 for (int v = 0; v < 4; ++v) { 87 verts[v].fKLM[0] = eval_line(verts[v].fPosition, fKlmEqs + 0, fSign); 88 verts[v].fKLM[1] = eval_line(verts[v].fPosition, fKlmEqs + 3, fSign); 89 verts[v].fKLM[2] = eval_line(verts[v].fPosition, fKlmEqs + 6, 1.f); 90 } 91 helper.recordDraw(target); 92 } 93 94 Geometry fGeometry; 95 SkScalar fKlmEqs[9]; 96 SkScalar fSign; 97 98 static const int kVertsPerCubic = 4; 99 static const int kIndicesPerCubic = 6; 100 101 typedef GrTestBatch INHERITED; 102}; 103 104/** 105 * This GM directly exercises effects that draw Bezier curves in the GPU backend. 106 */ 107class BezierCubicEffects : public GM { 108public: 109 BezierCubicEffects() { 110 this->setBGColor(0xFFFFFFFF); 111 } 112 113protected: 114 SkString onShortName() override { 115 return SkString("bezier_cubic_effects"); 116 } 117 118 SkISize onISize() override { 119 return SkISize::Make(800, 800); 120 } 121 122 void onDraw(SkCanvas* canvas) override { 123 GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget(); 124 if (NULL == rt) { 125 this->drawGpuOnlyMessage(canvas); 126 return; 127 } 128 GrContext* context = rt->getContext(); 129 if (NULL == context) { 130 return; 131 } 132 133 struct Vertex { 134 SkPoint fPosition; 135 float fKLM[4]; // The last value is ignored. The effect expects a vec4f. 136 }; 137 138 static const int kNumCubics = 15; 139 SkRandom rand; 140 141 // Mult by 3 for each edge effect type 142 int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumCubics*3))); 143 int numRows = SkScalarCeilToInt(SkIntToScalar(kNumCubics*3) / numCols); 144 SkScalar w = SkIntToScalar(rt->width()) / numCols; 145 SkScalar h = SkIntToScalar(rt->height()) / numRows; 146 int row = 0; 147 int col = 0; 148 static const GrColor color = 0xff000000; 149 150 for (int i = 0; i < kNumCubics; ++i) { 151 SkPoint baseControlPts[] = { 152 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}, 153 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}, 154 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}, 155 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)} 156 }; 157 for(int edgeType = 0; edgeType < kGrProcessorEdgeTypeCnt; ++edgeType) { 158 SkAutoTUnref<GrGeometryProcessor> gp; 159 { // scope to contain GrTestTarget 160 GrTestTarget tt; 161 context->getTestTarget(&tt); 162 if (NULL == tt.target()) { 163 continue; 164 } 165 GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType; 166 gp.reset(GrCubicEffect::Create(color, SkMatrix::I(), et, 167 *tt.target()->caps())); 168 if (!gp) { 169 continue; 170 } 171 } 172 173 SkScalar x = SkScalarMul(col, w); 174 SkScalar y = SkScalarMul(row, h); 175 SkPoint controlPts[] = { 176 {x + baseControlPts[0].fX, y + baseControlPts[0].fY}, 177 {x + baseControlPts[1].fX, y + baseControlPts[1].fY}, 178 {x + baseControlPts[2].fX, y + baseControlPts[2].fY}, 179 {x + baseControlPts[3].fX, y + baseControlPts[3].fY} 180 }; 181 SkPoint chopped[10]; 182 SkScalar klmEqs[9]; 183 SkScalar klmSigns[3]; 184 int cnt = GrPathUtils::chopCubicAtLoopIntersection(controlPts, 185 chopped, 186 klmEqs, 187 klmSigns); 188 189 SkPaint ctrlPtPaint; 190 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000); 191 for (int i = 0; i < 4; ++i) { 192 canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint); 193 } 194 195 SkPaint polyPaint; 196 polyPaint.setColor(0xffA0A0A0); 197 polyPaint.setStrokeWidth(0); 198 polyPaint.setStyle(SkPaint::kStroke_Style); 199 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 4, controlPts, polyPaint); 200 201 SkPaint choppedPtPaint; 202 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000); 203 204 for (int c = 0; c < cnt; ++c) { 205 SkPoint* pts = chopped + 3 * c; 206 207 for (int i = 0; i < 4; ++i) { 208 canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint); 209 } 210 211 SkRect bounds; 212 bounds.set(pts, 4); 213 214 SkPaint boundsPaint; 215 boundsPaint.setColor(0xff808080); 216 boundsPaint.setStrokeWidth(0); 217 boundsPaint.setStyle(SkPaint::kStroke_Style); 218 canvas->drawRect(bounds, boundsPaint); 219 220 GrTestTarget tt; 221 context->getTestTarget(&tt); 222 SkASSERT(tt.target()); 223 224 GrPipelineBuilder pipelineBuilder; 225 pipelineBuilder.setRenderTarget(rt); 226 227 BezierCubicOrConicTestBatch::Geometry geometry; 228 geometry.fColor = color; 229 geometry.fBounds = bounds; 230 231 SkAutoTUnref<GrDrawBatch> batch( 232 BezierCubicOrConicTestBatch::Create(gp, geometry, klmEqs, klmSigns[c])); 233 234 tt.target()->drawBatch(pipelineBuilder, batch); 235 } 236 ++col; 237 if (numCols == col) { 238 col = 0; 239 ++row; 240 } 241 } 242 } 243 } 244 245private: 246 typedef GM INHERITED; 247}; 248 249////////////////////////////////////////////////////////////////////////////// 250 251/** 252 * This GM directly exercises effects that draw Bezier curves in the GPU backend. 253 */ 254class BezierConicEffects : public GM { 255public: 256 BezierConicEffects() { 257 this->setBGColor(0xFFFFFFFF); 258 } 259 260protected: 261 SkString onShortName() override { 262 return SkString("bezier_conic_effects"); 263 } 264 265 SkISize onISize() override { 266 return SkISize::Make(800, 800); 267 } 268 269 270 void onDraw(SkCanvas* canvas) override { 271 GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget(); 272 if (NULL == rt) { 273 this->drawGpuOnlyMessage(canvas); 274 return; 275 } 276 GrContext* context = rt->getContext(); 277 if (NULL == context) { 278 return; 279 } 280 281 struct Vertex { 282 SkPoint fPosition; 283 float fKLM[4]; // The last value is ignored. The effect expects a vec4f. 284 }; 285 286 static const int kNumConics = 10; 287 SkRandom rand; 288 289 // Mult by 3 for each edge effect type 290 int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumConics*3))); 291 int numRows = SkScalarCeilToInt(SkIntToScalar(kNumConics*3) / numCols); 292 SkScalar w = SkIntToScalar(rt->width()) / numCols; 293 SkScalar h = SkIntToScalar(rt->height()) / numRows; 294 int row = 0; 295 int col = 0; 296 static const GrColor color = 0xff000000; 297 298 for (int i = 0; i < kNumConics; ++i) { 299 SkPoint baseControlPts[] = { 300 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}, 301 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}, 302 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)} 303 }; 304 SkScalar weight = rand.nextRangeF(0.f, 2.f); 305 for(int edgeType = 0; edgeType < kGrProcessorEdgeTypeCnt; ++edgeType) { 306 SkAutoTUnref<GrGeometryProcessor> gp; 307 { // scope to contain GrTestTarget 308 GrTestTarget tt; 309 context->getTestTarget(&tt); 310 if (NULL == tt.target()) { 311 continue; 312 } 313 GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType; 314 gp.reset(GrConicEffect::Create(color, SkMatrix::I(), et, 315 *tt.target()->caps(), SkMatrix::I(), false)); 316 if (!gp) { 317 continue; 318 } 319 } 320 321 SkScalar x = SkScalarMul(col, w); 322 SkScalar y = SkScalarMul(row, h); 323 SkPoint controlPts[] = { 324 {x + baseControlPts[0].fX, y + baseControlPts[0].fY}, 325 {x + baseControlPts[1].fX, y + baseControlPts[1].fY}, 326 {x + baseControlPts[2].fX, y + baseControlPts[2].fY} 327 }; 328 SkConic dst[4]; 329 SkScalar klmEqs[9]; 330 int cnt = chop_conic(controlPts, dst, weight); 331 GrPathUtils::getConicKLM(controlPts, weight, klmEqs); 332 333 SkPaint ctrlPtPaint; 334 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000); 335 for (int i = 0; i < 3; ++i) { 336 canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint); 337 } 338 339 SkPaint polyPaint; 340 polyPaint.setColor(0xffA0A0A0); 341 polyPaint.setStrokeWidth(0); 342 polyPaint.setStyle(SkPaint::kStroke_Style); 343 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint); 344 345 SkPaint choppedPtPaint; 346 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000); 347 348 for (int c = 0; c < cnt; ++c) { 349 SkPoint* pts = dst[c].fPts; 350 for (int i = 0; i < 3; ++i) { 351 canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint); 352 } 353 354 SkRect bounds; 355 //SkPoint bPts[] = {{0.f, 0.f}, {800.f, 800.f}}; 356 //bounds.set(bPts, 2); 357 bounds.set(pts, 3); 358 359 SkPaint boundsPaint; 360 boundsPaint.setColor(0xff808080); 361 boundsPaint.setStrokeWidth(0); 362 boundsPaint.setStyle(SkPaint::kStroke_Style); 363 canvas->drawRect(bounds, boundsPaint); 364 365 GrTestTarget tt; 366 context->getTestTarget(&tt); 367 SkASSERT(tt.target()); 368 369 GrPipelineBuilder pipelineBuilder; 370 pipelineBuilder.setRenderTarget(rt); 371 372 BezierCubicOrConicTestBatch::Geometry geometry; 373 geometry.fColor = color; 374 geometry.fBounds = bounds; 375 376 SkAutoTUnref<GrDrawBatch> batch( 377 BezierCubicOrConicTestBatch::Create(gp, geometry, klmEqs, 1.f)); 378 379 tt.target()->drawBatch(pipelineBuilder, batch); 380 } 381 ++col; 382 if (numCols == col) { 383 col = 0; 384 ++row; 385 } 386 } 387 } 388 } 389 390private: 391 // Uses the max curvature function for quads to estimate 392 // where to chop the conic. If the max curvature is not 393 // found along the curve segment it will return 1 and 394 // dst[0] is the original conic. If it returns 2 the dst[0] 395 // and dst[1] are the two new conics. 396 int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) { 397 SkScalar t = SkFindQuadMaxCurvature(src); 398 if (t == 0) { 399 if (dst) { 400 dst[0].set(src, weight); 401 } 402 return 1; 403 } else { 404 if (dst) { 405 SkConic conic; 406 conic.set(src, weight); 407 conic.chopAt(t, dst); 408 } 409 return 2; 410 } 411 } 412 413 // Calls split_conic on the entire conic and then once more on each subsection. 414 // Most cases will result in either 1 conic (chop point is not within t range) 415 // or 3 points (split once and then one subsection is split again). 416 int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) { 417 SkConic dstTemp[2]; 418 int conicCnt = split_conic(src, dstTemp, weight); 419 if (2 == conicCnt) { 420 int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW); 421 conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW); 422 } else { 423 dst[0] = dstTemp[0]; 424 } 425 return conicCnt; 426 } 427 428 typedef GM INHERITED; 429}; 430 431////////////////////////////////////////////////////////////////////////////// 432 433class BezierQuadTestBatch : public GrTestBatch { 434public: 435 struct Geometry : public GrTestBatch::Geometry { 436 SkRect fBounds; 437 }; 438 439 const char* name() const override { return "BezierQuadTestBatch"; } 440 441 static GrDrawBatch* Create(const GrGeometryProcessor* gp, const Geometry& geo, 442 const GrPathUtils::QuadUVMatrix& devToUV) { 443 return new BezierQuadTestBatch(gp, geo, devToUV); 444 } 445 446private: 447 BezierQuadTestBatch(const GrGeometryProcessor* gp, const Geometry& geo, 448 const GrPathUtils::QuadUVMatrix& devToUV) 449 : INHERITED(gp, geo.fBounds) 450 , fGeometry(geo) 451 , fDevToUV(devToUV) { 452 this->initClassID<BezierQuadTestBatch>(); 453 } 454 455 struct Vertex { 456 SkPoint fPosition; 457 float fKLM[4]; // The last value is ignored. The effect expects a vec4f. 458 }; 459 460 Geometry* geoData(int index) override { 461 SkASSERT(0 == index); 462 return &fGeometry; 463 } 464 465 const Geometry* geoData(int index) const override { 466 SkASSERT(0 == index); 467 return &fGeometry; 468 } 469 470 void generateGeometry(Target* target) override { 471 QuadHelper helper; 472 size_t vertexStride = this->geometryProcessor()->getVertexStride(); 473 SkASSERT(vertexStride == sizeof(Vertex)); 474 Vertex* verts = reinterpret_cast<Vertex*>(helper.init(target, vertexStride, 1)); 475 if (!verts) { 476 return; 477 } 478 verts[0].fPosition.setRectFan(fGeometry.fBounds.fLeft, fGeometry.fBounds.fTop, 479 fGeometry.fBounds.fRight, fGeometry.fBounds.fBottom, 480 sizeof(Vertex)); 481 fDevToUV.apply<4, sizeof(Vertex), sizeof(SkPoint)>(verts); 482 helper.recordDraw(target); 483 } 484 485 Geometry fGeometry; 486 GrPathUtils::QuadUVMatrix fDevToUV; 487 488 static const int kVertsPerCubic = 4; 489 static const int kIndicesPerCubic = 6; 490 491 typedef GrTestBatch INHERITED; 492}; 493 494/** 495 * This GM directly exercises effects that draw Bezier quad curves in the GPU backend. 496 */ 497class BezierQuadEffects : public GM { 498public: 499 BezierQuadEffects() { 500 this->setBGColor(0xFFFFFFFF); 501 } 502 503protected: 504 SkString onShortName() override { 505 return SkString("bezier_quad_effects"); 506 } 507 508 SkISize onISize() override { 509 return SkISize::Make(800, 800); 510 } 511 512 513 void onDraw(SkCanvas* canvas) override { 514 GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget(); 515 if (NULL == rt) { 516 this->drawGpuOnlyMessage(canvas); 517 return; 518 } 519 GrContext* context = rt->getContext(); 520 if (NULL == context) { 521 return; 522 } 523 524 struct Vertex { 525 SkPoint fPosition; 526 float fUV[4]; // The last two values are ignored. The effect expects a vec4f. 527 }; 528 529 static const int kNumQuads = 5; 530 SkRandom rand; 531 532 int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumQuads*3))); 533 int numRows = SkScalarCeilToInt(SkIntToScalar(kNumQuads*3) / numCols); 534 SkScalar w = SkIntToScalar(rt->width()) / numCols; 535 SkScalar h = SkIntToScalar(rt->height()) / numRows; 536 int row = 0; 537 int col = 0; 538 static const GrColor color = 0xff000000; 539 540 for (int i = 0; i < kNumQuads; ++i) { 541 SkPoint baseControlPts[] = { 542 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}, 543 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}, 544 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)} 545 }; 546 for(int edgeType = 0; edgeType < kGrProcessorEdgeTypeCnt; ++edgeType) { 547 SkAutoTUnref<GrGeometryProcessor> gp; 548 { // scope to contain GrTestTarget 549 GrTestTarget tt; 550 context->getTestTarget(&tt); 551 if (NULL == tt.target()) { 552 continue; 553 } 554 GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType; 555 gp.reset(GrQuadEffect::Create(color, SkMatrix::I(), et, 556 *tt.target()->caps(), SkMatrix::I(), false)); 557 if (!gp) { 558 continue; 559 } 560 } 561 562 SkScalar x = SkScalarMul(col, w); 563 SkScalar y = SkScalarMul(row, h); 564 SkPoint controlPts[] = { 565 {x + baseControlPts[0].fX, y + baseControlPts[0].fY}, 566 {x + baseControlPts[1].fX, y + baseControlPts[1].fY}, 567 {x + baseControlPts[2].fX, y + baseControlPts[2].fY} 568 }; 569 SkPoint chopped[5]; 570 int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped); 571 572 SkPaint ctrlPtPaint; 573 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000); 574 for (int i = 0; i < 3; ++i) { 575 canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint); 576 } 577 578 SkPaint polyPaint; 579 polyPaint.setColor(0xffA0A0A0); 580 polyPaint.setStrokeWidth(0); 581 polyPaint.setStyle(SkPaint::kStroke_Style); 582 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint); 583 584 SkPaint choppedPtPaint; 585 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000); 586 587 for (int c = 0; c < cnt; ++c) { 588 SkPoint* pts = chopped + 2 * c; 589 590 for (int i = 0; i < 3; ++i) { 591 canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint); 592 } 593 594 SkRect bounds; 595 bounds.set(pts, 3); 596 597 SkPaint boundsPaint; 598 boundsPaint.setColor(0xff808080); 599 boundsPaint.setStrokeWidth(0); 600 boundsPaint.setStyle(SkPaint::kStroke_Style); 601 canvas->drawRect(bounds, boundsPaint); 602 603 GrTestTarget tt; 604 context->getTestTarget(&tt); 605 SkASSERT(tt.target()); 606 607 GrPipelineBuilder pipelineBuilder; 608 pipelineBuilder.setRenderTarget(rt); 609 610 GrPathUtils::QuadUVMatrix DevToUV(pts); 611 612 BezierQuadTestBatch::Geometry geometry; 613 geometry.fColor = color; 614 geometry.fBounds = bounds; 615 616 SkAutoTUnref<GrDrawBatch> batch(BezierQuadTestBatch::Create(gp, geometry, 617 DevToUV)); 618 619 tt.target()->drawBatch(pipelineBuilder, batch); 620 } 621 ++col; 622 if (numCols == col) { 623 col = 0; 624 ++row; 625 } 626 } 627 } 628 } 629 630private: 631 typedef GM INHERITED; 632}; 633 634DEF_GM(return new BezierCubicEffects;) 635DEF_GM(return new BezierConicEffects;) 636DEF_GM(return new BezierQuadEffects;) 637} 638 639#endif 640