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