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