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