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