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