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