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