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