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