1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 // This test only works with the GPU backend. 9 10 #include "gm.h" 11 #include "sk_tool_utils.h" 12 13 #include "GrContext.h" 14 #include "GrMemoryPool.h" 15 #include "GrOpFlushState.h" 16 #include "GrPathUtils.h" 17 #include "GrRenderTargetContextPriv.h" 18 #include "SkColorPriv.h" 19 #include "SkGeometry.h" 20 #include "SkPoint3.h" 21 #include "SkPointPriv.h" 22 #include "effects/GrBezierEffect.h" 23 #include "ops/GrMeshDrawOp.h" 24 25 namespace skiagm { 26 27 class BezierTestOp : public GrMeshDrawOp { 28 public: fixedFunctionFlags() const29 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } 30 finalize(const GrCaps & caps,const GrAppliedClip * clip)31 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override { 32 return fProcessorSet.finalize(fColor, GrProcessorAnalysisCoverage::kSingleChannel, clip, 33 false, caps, &fColor); 34 } 35 visitProxies(const VisitProxyFunc & func,VisitorType) const36 void visitProxies(const VisitProxyFunc& func, VisitorType) const override { 37 fProcessorSet.visitProxies(func); 38 } 39 40 protected: BezierTestOp(sk_sp<const GrGeometryProcessor> gp,const SkRect & rect,const SkPMColor4f & color,int32_t classID)41 BezierTestOp(sk_sp<const GrGeometryProcessor> gp, const SkRect& rect, const SkPMColor4f& color, 42 int32_t classID) 43 : INHERITED(classID) 44 , fRect(rect) 45 , fColor(color) 46 , fGeometryProcessor(std::move(gp)) 47 , fProcessorSet(SkBlendMode::kSrc) { 48 this->setBounds(rect, HasAABloat::kYes, IsZeroArea::kNo); 49 } 50 makePipeline(Target * target)51 Target::PipelineAndFixedDynamicState makePipeline(Target* target) { 52 return target->makePipeline(0, std::move(fProcessorSet), target->detachAppliedClip()); 53 } 54 gp() const55 sk_sp<const GrGeometryProcessor> gp() const { return fGeometryProcessor; } 56 rect() const57 const SkRect& rect() const { return fRect; } color() const58 const SkPMColor4f& color() const { return fColor; } 59 60 private: 61 SkRect fRect; 62 SkPMColor4f fColor; 63 sk_sp<const GrGeometryProcessor> fGeometryProcessor; 64 GrProcessorSet fProcessorSet; 65 66 typedef GrMeshDrawOp INHERITED; 67 }; 68 69 /** 70 * This GM directly exercises effects that draw Bezier curves in the GPU backend. 71 */ 72 73 class BezierConicTestOp : public BezierTestOp { 74 public: 75 DEFINE_OP_CLASS_ID 76 name() const77 const char* name() const override { return "BezierConicTestOp"; } 78 Make(GrContext * context,sk_sp<const GrGeometryProcessor> gp,const SkRect & rect,const SkPMColor4f & color,const SkMatrix & klm)79 static std::unique_ptr<GrDrawOp> Make(GrContext* context, 80 sk_sp<const GrGeometryProcessor> gp, 81 const SkRect& rect, 82 const SkPMColor4f& color, 83 const SkMatrix& klm) { 84 GrOpMemoryPool* pool = context->contextPriv().opMemoryPool(); 85 86 return pool->allocate<BezierConicTestOp>(std::move(gp), rect, color, klm); 87 } 88 89 private: 90 friend class ::GrOpMemoryPool; // for ctor 91 BezierConicTestOp(sk_sp<const GrGeometryProcessor> gp,const SkRect & rect,const SkPMColor4f & color,const SkMatrix & klm)92 BezierConicTestOp(sk_sp<const GrGeometryProcessor> gp, const SkRect& rect, 93 const SkPMColor4f& color, const SkMatrix& klm) 94 : INHERITED(std::move(gp), rect, color, ClassID()), fKLM(klm) {} 95 96 struct Vertex { 97 SkPoint fPosition; 98 float fKLM[4]; // The last value is ignored. The effect expects a vec4f. 99 }; 100 onPrepareDraws(Target * target)101 void onPrepareDraws(Target* target) override { 102 SkASSERT(this->gp()->vertexStride() == sizeof(Vertex)); 103 QuadHelper helper(target, sizeof(Vertex), 1); 104 Vertex* verts = reinterpret_cast<Vertex*>(helper.vertices()); 105 if (!verts) { 106 return; 107 } 108 SkRect rect = this->rect(); 109 SkPointPriv::SetRectTriStrip(&verts[0].fPosition, rect.fLeft, rect.fTop, rect.fRight, 110 rect.fBottom, sizeof(Vertex)); 111 for (int v = 0; v < 4; ++v) { 112 SkPoint3 pt3 = {verts[v].fPosition.x(), verts[v].fPosition.y(), 1.f}; 113 fKLM.mapHomogeneousPoints((SkPoint3* ) verts[v].fKLM, &pt3, 1); 114 } 115 auto pipe = this->makePipeline(target); 116 helper.recordDraw(target, this->gp(), pipe.fPipeline, pipe.fFixedDynamicState); 117 } 118 119 SkMatrix fKLM; 120 121 static constexpr int kVertsPerCubic = 4; 122 static constexpr int kIndicesPerCubic = 6; 123 124 typedef BezierTestOp INHERITED; 125 }; 126 127 128 /** 129 * This GM directly exercises effects that draw Bezier curves in the GPU backend. 130 */ 131 class BezierConicEffects : public GM { 132 public: BezierConicEffects()133 BezierConicEffects() { 134 this->setBGColor(0xFFFFFFFF); 135 } 136 137 protected: onShortName()138 SkString onShortName() override { 139 return SkString("bezier_conic_effects"); 140 } 141 onISize()142 SkISize onISize() override { 143 return SkISize::Make(800, 800); 144 } 145 146 onDraw(SkCanvas * canvas)147 void onDraw(SkCanvas* canvas) override { 148 GrRenderTargetContext* renderTargetContext = 149 canvas->internal_private_accessTopLayerRenderTargetContext(); 150 if (!renderTargetContext) { 151 skiagm::GM::DrawGpuOnlyMessage(canvas); 152 return; 153 } 154 155 GrContext* context = canvas->getGrContext(); 156 if (!context) { 157 return; 158 } 159 160 struct Vertex { 161 SkPoint fPosition; 162 float fKLM[4]; // The last value is ignored. The effect expects a vec4f. 163 }; 164 165 constexpr int kNumConics = 10; 166 SkRandom rand; 167 168 // Mult by 3 for each edge effect type 169 int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumConics*3))); 170 int numRows = SkScalarCeilToInt(SkIntToScalar(kNumConics*3) / numCols); 171 SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols; 172 SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows; 173 int row = 0; 174 int col = 0; 175 SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(0xff000000); 176 177 for (int i = 0; i < kNumConics; ++i) { 178 SkPoint baseControlPts[] = { 179 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}, 180 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}, 181 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)} 182 }; 183 SkScalar weight = rand.nextRangeF(0.f, 2.f); 184 for(int edgeType = 0; edgeType < kGrClipEdgeTypeCnt; ++edgeType) { 185 sk_sp<GrGeometryProcessor> gp; 186 GrClipEdgeType et = (GrClipEdgeType)edgeType; 187 gp = GrConicEffect::Make(color, SkMatrix::I(), et, *context->contextPriv().caps(), 188 SkMatrix::I(), false); 189 if (!gp) { 190 continue; 191 } 192 193 SkScalar x = col * w; 194 SkScalar y = row * h; 195 SkPoint controlPts[] = { 196 {x + baseControlPts[0].fX, y + baseControlPts[0].fY}, 197 {x + baseControlPts[1].fX, y + baseControlPts[1].fY}, 198 {x + baseControlPts[2].fX, y + baseControlPts[2].fY} 199 }; 200 SkConic dst[4]; 201 SkMatrix klm; 202 int cnt = chop_conic(controlPts, dst, weight); 203 GrPathUtils::getConicKLM(controlPts, weight, &klm); 204 205 SkPaint ctrlPtPaint; 206 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000); 207 for (int i = 0; i < 3; ++i) { 208 canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint); 209 } 210 211 SkPaint polyPaint; 212 polyPaint.setColor(0xffA0A0A0); 213 polyPaint.setStrokeWidth(0); 214 polyPaint.setStyle(SkPaint::kStroke_Style); 215 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint); 216 217 SkPaint choppedPtPaint; 218 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000); 219 220 for (int c = 0; c < cnt; ++c) { 221 SkPoint* pts = dst[c].fPts; 222 for (int i = 0; i < 3; ++i) { 223 canvas->drawCircle(pts[i], 3.f, choppedPtPaint); 224 } 225 226 SkRect bounds; 227 //SkPoint bPts[] = {{0.f, 0.f}, {800.f, 800.f}}; 228 //bounds.set(bPts, 2); 229 bounds.set(pts, 3); 230 231 SkPaint boundsPaint; 232 boundsPaint.setColor(0xff808080); 233 boundsPaint.setStrokeWidth(0); 234 boundsPaint.setStyle(SkPaint::kStroke_Style); 235 canvas->drawRect(bounds, boundsPaint); 236 237 std::unique_ptr<GrDrawOp> op = BezierConicTestOp::Make(context, gp, bounds, 238 color, klm); 239 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op)); 240 } 241 ++col; 242 if (numCols == col) { 243 col = 0; 244 ++row; 245 } 246 } 247 } 248 } 249 250 private: 251 // Uses the max curvature function for quads to estimate 252 // where to chop the conic. If the max curvature is not 253 // found along the curve segment it will return 1 and 254 // dst[0] is the original conic. If it returns 2 the dst[0] 255 // and dst[1] are the two new conics. split_conic(const SkPoint src[3],SkConic dst[2],const SkScalar weight)256 int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) { 257 SkScalar t = SkFindQuadMaxCurvature(src); 258 if (t == 0 || t == 1) { 259 if (dst) { 260 dst[0].set(src, weight); 261 } 262 return 1; 263 } else { 264 if (dst) { 265 SkConic conic; 266 conic.set(src, weight); 267 if (!conic.chopAt(t, dst)) { 268 dst[0].set(src, weight); 269 return 1; 270 } 271 } 272 return 2; 273 } 274 } 275 276 // Calls split_conic on the entire conic and then once more on each subsection. 277 // Most cases will result in either 1 conic (chop point is not within t range) 278 // or 3 points (split once and then one subsection is split again). chop_conic(const SkPoint src[3],SkConic dst[4],const SkScalar weight)279 int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) { 280 SkConic dstTemp[2]; 281 int conicCnt = split_conic(src, dstTemp, weight); 282 if (2 == conicCnt) { 283 int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW); 284 conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW); 285 } else { 286 dst[0] = dstTemp[0]; 287 } 288 return conicCnt; 289 } 290 291 typedef GM INHERITED; 292 }; 293 294 ////////////////////////////////////////////////////////////////////////////// 295 296 class BezierQuadTestOp : public BezierTestOp { 297 public: 298 DEFINE_OP_CLASS_ID name() const299 const char* name() const override { return "BezierQuadTestOp"; } 300 Make(GrContext * context,sk_sp<const GrGeometryProcessor> gp,const SkRect & rect,const SkPMColor4f & color,const GrPathUtils::QuadUVMatrix & devToUV)301 static std::unique_ptr<GrDrawOp> Make(GrContext* context, 302 sk_sp<const GrGeometryProcessor> gp, 303 const SkRect& rect, 304 const SkPMColor4f& color, 305 const GrPathUtils::QuadUVMatrix& devToUV) { 306 GrOpMemoryPool* pool = context->contextPriv().opMemoryPool(); 307 308 return pool->allocate<BezierQuadTestOp>(std::move(gp), rect, color, devToUV); 309 } 310 311 private: 312 friend class ::GrOpMemoryPool; // for ctor 313 BezierQuadTestOp(sk_sp<const GrGeometryProcessor> gp,const SkRect & rect,const SkPMColor4f & color,const GrPathUtils::QuadUVMatrix & devToUV)314 BezierQuadTestOp(sk_sp<const GrGeometryProcessor> gp, const SkRect& rect, 315 const SkPMColor4f& color, const GrPathUtils::QuadUVMatrix& devToUV) 316 : INHERITED(std::move(gp), rect, color, ClassID()), fDevToUV(devToUV) {} 317 318 struct Vertex { 319 SkPoint fPosition; 320 float fKLM[4]; // The last value is ignored. The effect expects a vec4f. 321 }; 322 onPrepareDraws(Target * target)323 void onPrepareDraws(Target* target) override { 324 SkASSERT(this->gp()->vertexStride() == sizeof(Vertex)); 325 QuadHelper helper(target, sizeof(Vertex), 1); 326 Vertex* verts = reinterpret_cast<Vertex*>(helper.vertices()); 327 if (!verts) { 328 return; 329 } 330 SkRect rect = this->rect(); 331 SkPointPriv::SetRectTriStrip(&verts[0].fPosition, rect, sizeof(Vertex)); 332 fDevToUV.apply(verts, 4, sizeof(Vertex), sizeof(SkPoint)); 333 auto pipe = this->makePipeline(target); 334 helper.recordDraw(target, this->gp(), pipe.fPipeline, pipe.fFixedDynamicState); 335 } 336 337 GrPathUtils::QuadUVMatrix fDevToUV; 338 339 static constexpr int kVertsPerCubic = 4; 340 static constexpr int kIndicesPerCubic = 6; 341 342 typedef BezierTestOp INHERITED; 343 }; 344 345 /** 346 * This GM directly exercises effects that draw Bezier quad curves in the GPU backend. 347 */ 348 class BezierQuadEffects : public GM { 349 public: BezierQuadEffects()350 BezierQuadEffects() { 351 this->setBGColor(0xFFFFFFFF); 352 } 353 354 protected: onShortName()355 SkString onShortName() override { 356 return SkString("bezier_quad_effects"); 357 } 358 onISize()359 SkISize onISize() override { 360 return SkISize::Make(800, 800); 361 } 362 363 onDraw(SkCanvas * canvas)364 void onDraw(SkCanvas* canvas) override { 365 GrRenderTargetContext* renderTargetContext = 366 canvas->internal_private_accessTopLayerRenderTargetContext(); 367 if (!renderTargetContext) { 368 skiagm::GM::DrawGpuOnlyMessage(canvas); 369 return; 370 } 371 372 GrContext* context = canvas->getGrContext(); 373 if (!context) { 374 return; 375 } 376 377 struct Vertex { 378 SkPoint fPosition; 379 float fUV[4]; // The last two values are ignored. The effect expects a vec4f. 380 }; 381 382 constexpr int kNumQuads = 5; 383 SkRandom rand; 384 385 int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumQuads*3))); 386 int numRows = SkScalarCeilToInt(SkIntToScalar(kNumQuads*3) / numCols); 387 SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols; 388 SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows; 389 int row = 0; 390 int col = 0; 391 SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(0xff000000); 392 393 for (int i = 0; i < kNumQuads; ++i) { 394 SkPoint baseControlPts[] = { 395 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}, 396 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}, 397 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)} 398 }; 399 for(int edgeType = 0; edgeType < kGrClipEdgeTypeCnt; ++edgeType) { 400 sk_sp<GrGeometryProcessor> gp; 401 GrClipEdgeType et = (GrClipEdgeType)edgeType; 402 gp = GrQuadEffect::Make(color, SkMatrix::I(), et, *context->contextPriv().caps(), 403 SkMatrix::I(), false); 404 if (!gp) { 405 continue; 406 } 407 408 SkScalar x = col * w; 409 SkScalar y = row * h; 410 SkPoint controlPts[] = { 411 {x + baseControlPts[0].fX, y + baseControlPts[0].fY}, 412 {x + baseControlPts[1].fX, y + baseControlPts[1].fY}, 413 {x + baseControlPts[2].fX, y + baseControlPts[2].fY} 414 }; 415 SkPoint chopped[5]; 416 int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped); 417 418 SkPaint ctrlPtPaint; 419 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000); 420 for (int i = 0; i < 3; ++i) { 421 canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint); 422 } 423 424 SkPaint polyPaint; 425 polyPaint.setColor(0xffA0A0A0); 426 polyPaint.setStrokeWidth(0); 427 polyPaint.setStyle(SkPaint::kStroke_Style); 428 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint); 429 430 SkPaint choppedPtPaint; 431 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000); 432 433 for (int c = 0; c < cnt; ++c) { 434 SkPoint* pts = chopped + 2 * c; 435 436 for (int i = 0; i < 3; ++i) { 437 canvas->drawCircle(pts[i], 3.f, choppedPtPaint); 438 } 439 440 SkRect bounds; 441 bounds.set(pts, 3); 442 443 SkPaint boundsPaint; 444 boundsPaint.setColor(0xff808080); 445 boundsPaint.setStrokeWidth(0); 446 boundsPaint.setStyle(SkPaint::kStroke_Style); 447 canvas->drawRect(bounds, boundsPaint); 448 449 GrPaint grPaint; 450 grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc)); 451 452 GrPathUtils::QuadUVMatrix DevToUV(pts); 453 454 std::unique_ptr<GrDrawOp> op = BezierQuadTestOp::Make(context, gp, 455 bounds, color, DevToUV); 456 renderTargetContext->priv().testingOnly_addDrawOp(std::move(op)); 457 } 458 ++col; 459 if (numCols == col) { 460 col = 0; 461 ++row; 462 } 463 } 464 } 465 } 466 467 private: 468 typedef GM INHERITED; 469 }; 470 471 DEF_GM(return new BezierConicEffects;) 472 DEF_GM(return new BezierQuadEffects;) 473 } 474