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