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