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/gm.h" 11 #include "include/core/SkBlendMode.h" 12 #include "include/core/SkCanvas.h" 13 #include "include/core/SkMatrix.h" 14 #include "include/core/SkPaint.h" 15 #include "include/core/SkPoint.h" 16 #include "include/core/SkPoint3.h" 17 #include "include/core/SkRect.h" 18 #include "include/core/SkRefCnt.h" 19 #include "include/core/SkScalar.h" 20 #include "include/core/SkSize.h" 21 #include "include/core/SkString.h" 22 #include "include/core/SkTypes.h" 23 #include "include/gpu/GrRecordingContext.h" 24 #include "include/private/GrTypesPriv.h" 25 #include "include/private/SkColorData.h" 26 #include "include/utils/SkRandom.h" 27 #include "src/core/SkCanvasPriv.h" 28 #include "src/core/SkGeometry.h" 29 #include "src/core/SkPointPriv.h" 30 #include "src/gpu/GrCaps.h" 31 #include "src/gpu/GrDirectContextPriv.h" 32 #include "src/gpu/GrGeometryProcessor.h" 33 #include "src/gpu/GrMemoryPool.h" 34 #include "src/gpu/GrOpFlushState.h" 35 #include "src/gpu/GrOpsRenderPass.h" 36 #include "src/gpu/GrPaint.h" 37 #include "src/gpu/GrProcessorAnalysis.h" 38 #include "src/gpu/GrProcessorSet.h" 39 #include "src/gpu/GrProgramInfo.h" 40 #include "src/gpu/GrRecordingContextPriv.h" 41 #include "src/gpu/GrUserStencilSettings.h" 42 #include "src/gpu/effects/GrBezierEffect.h" 43 #include "src/gpu/effects/GrPorterDuffXferProcessor.h" 44 #include "src/gpu/geometry/GrPathUtils.h" 45 #include "src/gpu/ops/GrDrawOp.h" 46 #include "src/gpu/ops/GrMeshDrawOp.h" 47 #include "src/gpu/ops/GrOp.h" 48 #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h" 49 #include "src/gpu/v1/SurfaceDrawContext_v1.h" 50 51 #include <memory> 52 #include <utility> 53 54 class GrAppliedClip; 55 56 namespace skiagm { 57 58 class BezierTestOp : public GrMeshDrawOp { 59 public: fixedFunctionFlags() const60 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } 61 finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)62 GrProcessorSet::Analysis finalize( 63 const GrCaps& caps, const GrAppliedClip* clip, GrClampType clampType) override { 64 return fProcessorSet.finalize( 65 fColor, GrProcessorAnalysisCoverage::kSingleChannel, clip, 66 &GrUserStencilSettings::kUnused, caps, clampType, &fColor); 67 } 68 visitProxies(const GrVisitProxyFunc & func) const69 void visitProxies(const GrVisitProxyFunc& func) const override { 70 if (fProgramInfo) { 71 fProgramInfo->visitFPProxies(func); 72 } else { 73 fProcessorSet.visitProxies(func); 74 } 75 } 76 77 protected: BezierTestOp(const SkRect & rect,const SkPMColor4f & color,int32_t classID)78 BezierTestOp(const SkRect& rect, const SkPMColor4f& color, int32_t classID) 79 : INHERITED(classID) 80 , fRect(rect) 81 , fColor(color) 82 , fProcessorSet(SkBlendMode::kSrc) { 83 this->setBounds(rect, HasAABloat::kYes, IsHairline::kNo); 84 } 85 86 virtual GrGeometryProcessor* makeGP(const GrCaps& caps, SkArenaAlloc* arena) = 0; 87 programInfo()88 GrProgramInfo* programInfo() override { return fProgramInfo; } 89 onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)90 void onCreateProgramInfo(const GrCaps* caps, 91 SkArenaAlloc* arena, 92 const GrSurfaceProxyView& writeView, 93 bool usesMSAASurface, 94 GrAppliedClip&& appliedClip, 95 const GrDstProxyView& dstProxyView, 96 GrXferBarrierFlags renderPassXferBarriers, 97 GrLoadOp colorLoadOp) override { 98 auto gp = this->makeGP(*caps, arena); 99 if (!gp) { 100 return; 101 } 102 103 GrPipeline::InputFlags flags = GrPipeline::InputFlags::kNone; 104 105 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView, 106 usesMSAASurface, 107 std::move(appliedClip), 108 dstProxyView, gp, 109 std::move(fProcessorSet), 110 GrPrimitiveType::kTriangles, 111 renderPassXferBarriers, 112 colorLoadOp, 113 flags); 114 } 115 onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)116 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) final { 117 if (!fProgramInfo) { 118 this->createProgramInfo(flushState); 119 } 120 121 if (!fProgramInfo) { 122 return; 123 } 124 125 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds); 126 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline()); 127 flushState->drawMesh(*fMesh); 128 } 129 rect() const130 const SkRect& rect() const { return fRect; } color() const131 const SkPMColor4f& color() const { return fColor; } 132 133 protected: 134 GrSimpleMesh* fMesh = nullptr; // filled in by the derived classes 135 136 private: 137 SkRect fRect; 138 SkPMColor4f fColor; 139 GrProcessorSet fProcessorSet; 140 GrProgramInfo* fProgramInfo = nullptr; 141 142 using INHERITED = GrMeshDrawOp; 143 }; 144 145 /** 146 * This GM directly exercises effects that draw Bezier curves in the GPU backend. 147 */ 148 class BezierConicTestOp : public BezierTestOp { 149 public: 150 DEFINE_OP_CLASS_ID 151 name() const152 const char* name() const final { return "BezierConicTestOp"; } 153 Make(GrRecordingContext * context,const SkRect & rect,const SkPMColor4f & color,const SkMatrix & klm)154 static GrOp::Owner Make(GrRecordingContext* context, 155 const SkRect& rect, 156 const SkPMColor4f& color, 157 const SkMatrix& klm) { 158 return GrOp::Make<BezierConicTestOp>(context, rect, color, klm); 159 } 160 161 private: 162 friend class ::GrOp; // for ctor 163 BezierConicTestOp(const SkRect & rect,const SkPMColor4f & color,const SkMatrix & klm)164 BezierConicTestOp(const SkRect& rect, const SkPMColor4f& color, const SkMatrix& klm) 165 : INHERITED(rect, color, ClassID()) 166 , fKLM(klm) {} 167 168 struct Vertex { 169 SkPoint fPosition; 170 float fKLM[4]; // The last value is ignored. The effect expects a vec4f. 171 }; 172 makeGP(const GrCaps & caps,SkArenaAlloc * arena)173 GrGeometryProcessor* makeGP(const GrCaps& caps, SkArenaAlloc* arena) final { 174 auto tmp = GrConicEffect::Make(arena, this->color(), SkMatrix::I(), caps, SkMatrix::I(), 175 false); 176 if (!tmp) { 177 return nullptr; 178 } 179 SkASSERT(tmp->vertexStride() == sizeof(Vertex)); 180 return tmp; 181 } 182 onPrepareDraws(GrMeshDrawTarget * target)183 void onPrepareDraws(GrMeshDrawTarget* target) final { 184 QuadHelper helper(target, sizeof(Vertex), 1); 185 Vertex* verts = reinterpret_cast<Vertex*>(helper.vertices()); 186 if (!verts) { 187 return; 188 } 189 SkRect rect = this->rect(); 190 SkPointPriv::SetRectTriStrip(&verts[0].fPosition, rect, sizeof(Vertex)); 191 for (int v = 0; v < 4; ++v) { 192 SkPoint3 pt3 = {verts[v].fPosition.x(), verts[v].fPosition.y(), 1.f}; 193 fKLM.mapHomogeneousPoints((SkPoint3* ) verts[v].fKLM, &pt3, 1); 194 } 195 196 fMesh = helper.mesh(); 197 } 198 199 SkMatrix fKLM; 200 201 inline static constexpr int kVertsPerCubic = 4; 202 inline static constexpr int kIndicesPerCubic = 6; 203 204 using INHERITED = BezierTestOp; 205 }; 206 207 208 /** 209 * This GM directly exercises effects that draw Bezier curves in the GPU backend. 210 */ 211 class BezierConicEffects : public GpuGM { 212 public: BezierConicEffects()213 BezierConicEffects() { 214 this->setBGColor(0xFFFFFFFF); 215 } 216 217 protected: 218 static const int kNumConics = 10; 219 static const int kCellWidth = 128; 220 static const int kCellHeight = 128; 221 onShortName()222 SkString onShortName() override { 223 return SkString("bezier_conic_effects"); 224 } 225 onISize()226 SkISize onISize() override { 227 return SkISize::Make(kCellWidth, kNumConics*kCellHeight); 228 } 229 onDraw(GrRecordingContext * rContext,SkCanvas * canvas,SkString * errorMsg)230 DrawResult onDraw(GrRecordingContext* rContext, SkCanvas* canvas, SkString* errorMsg) override { 231 auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas); 232 if (!sdc) { 233 *errorMsg = kErrorMsg_DrawSkippedGpuOnly; 234 return DrawResult::kSkip; 235 } 236 237 const SkScalar w = kCellWidth, h = kCellHeight; 238 const SkPMColor4f kOpaqueBlack = SkPMColor4f::FromBytes_RGBA(0xff000000); 239 240 const SkPoint baseControlPts[kNumConics][3] = { 241 { { 0.31f * w, 0.01f * h}, { 0.48f * w, 0.74f * h }, { 0.19f * w, 0.33f * h } }, 242 { { 0.00f * w, 0.07f * h}, { 0.30f * w, 0.70f * h }, { 0.47f * w, 0.37f * h } }, 243 { { 0.15f * w, 0.23f * h}, { 0.49f * w, 0.87f * h }, { 0.85f * w, 0.66f * h } }, 244 { { 0.09f * w, 0.15f * h}, { 0.42f * w, 0.33f * h }, { 0.17f * w, 0.38f * h } }, 245 { { 0.98f * w, 0.54f * h}, { 0.83f * w, 0.91f * h }, { 0.62f * w, 0.40f * h } }, 246 { { 0.96f * w, 0.65f * h}, { 0.03f * w, 0.79f * h }, { 0.24f * w, 0.56f * h } }, 247 { { 0.57f * w, 0.12f * h}, { 0.33f * w, 0.67f * h }, { 0.59f * w, 0.33f * h } }, 248 { { 0.12f * w, 0.72f * h}, { 0.69f * w, 0.85f * h }, { 0.46f * w, 0.32f * h } }, 249 { { 0.27f * w, 0.49f * h}, { 0.41f * w, 0.02f * h }, { 0.11f * w, 0.42f * h } }, 250 { { 0.40f * w, 0.13f * h}, { 0.83f * w, 0.30f * h }, { 0.31f * w, 0.68f * h } }, 251 }; 252 const SkScalar weights[kNumConics] = { 0.62f, 0.01f, 0.95f, 1.48f, 0.37f, 253 0.66f, 0.15f, 0.14f, 0.61f, 1.4f }; 254 255 SkPaint ctrlPtPaint; 256 ctrlPtPaint.setColor(SK_ColorRED); 257 258 SkPaint choppedPtPaint; 259 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000); 260 261 SkPaint polyPaint; 262 polyPaint.setColor(0xffA0A0A0); 263 polyPaint.setStrokeWidth(0); 264 polyPaint.setStyle(SkPaint::kStroke_Style); 265 266 SkPaint boundsPaint; 267 boundsPaint.setColor(0xff808080); 268 boundsPaint.setStrokeWidth(0); 269 boundsPaint.setStyle(SkPaint::kStroke_Style); 270 271 272 for (int row = 0; row < kNumConics; ++row) { 273 SkScalar x = 0; 274 SkScalar y = row * h; 275 SkPoint controlPts[] = { 276 {x + baseControlPts[row][0].fX, y + baseControlPts[row][0].fY}, 277 {x + baseControlPts[row][1].fX, y + baseControlPts[row][1].fY}, 278 {x + baseControlPts[row][2].fX, y + baseControlPts[row][2].fY} 279 }; 280 281 for (int i = 0; i < 3; ++i) { 282 canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint); 283 } 284 285 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint); 286 287 SkConic dst[4]; 288 SkMatrix klm; 289 int cnt = ChopConic(controlPts, dst, weights[row]); 290 GrPathUtils::getConicKLM(controlPts, weights[row], &klm); 291 292 for (int c = 0; c < cnt; ++c) { 293 SkPoint* pts = dst[c].fPts; 294 for (int i = 0; i < 3; ++i) { 295 canvas->drawCircle(pts[i], 3.f, choppedPtPaint); 296 } 297 298 SkRect bounds; 299 bounds.setBounds(pts, 3); 300 301 canvas->drawRect(bounds, boundsPaint); 302 303 GrOp::Owner op = BezierConicTestOp::Make(rContext, bounds, 304 kOpaqueBlack, klm); 305 sdc->addDrawOp(std::move(op)); 306 } 307 } 308 309 return DrawResult::kOk; 310 } 311 312 private: 313 // Uses the max curvature function for quads to estimate 314 // where to chop the conic. If the max curvature is not 315 // found along the curve segment it will return 1 and 316 // dst[0] is the original conic. If it returns 2 the dst[0] 317 // and dst[1] are the two new conics. SplitConic(const SkPoint src[3],SkConic dst[2],const SkScalar weight)318 static int SplitConic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) { 319 SkScalar t = SkFindQuadMaxCurvature(src); 320 if (t == 0 || t == 1) { 321 if (dst) { 322 dst[0].set(src, weight); 323 } 324 return 1; 325 } else { 326 if (dst) { 327 SkConic conic; 328 conic.set(src, weight); 329 if (!conic.chopAt(t, dst)) { 330 dst[0].set(src, weight); 331 return 1; 332 } 333 } 334 return 2; 335 } 336 } 337 338 // Calls SplitConic on the entire conic and then once more on each subsection. 339 // Most cases will result in either 1 conic (chop point is not within t range) 340 // or 3 points (split once and then one subsection is split again). ChopConic(const SkPoint src[3],SkConic dst[4],const SkScalar weight)341 static int ChopConic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) { 342 SkConic dstTemp[2]; 343 int conicCnt = SplitConic(src, dstTemp, weight); 344 if (2 == conicCnt) { 345 int conicCnt2 = SplitConic(dstTemp[0].fPts, dst, dstTemp[0].fW); 346 conicCnt = conicCnt2 + SplitConic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW); 347 } else { 348 dst[0] = dstTemp[0]; 349 } 350 return conicCnt; 351 } 352 353 using INHERITED = GM; 354 }; 355 356 ////////////////////////////////////////////////////////////////////////////// 357 358 class BezierQuadTestOp : public BezierTestOp { 359 public: 360 DEFINE_OP_CLASS_ID name() const361 const char* name() const override { return "BezierQuadTestOp"; } 362 Make(GrRecordingContext * context,const SkRect & rect,const SkPMColor4f & color,const GrPathUtils::QuadUVMatrix & devToUV)363 static GrOp::Owner Make(GrRecordingContext* context, 364 const SkRect& rect, 365 const SkPMColor4f& color, 366 const GrPathUtils::QuadUVMatrix& devToUV) { 367 return GrOp::Make<BezierQuadTestOp>(context, rect, color, devToUV); 368 } 369 370 private: 371 friend class ::GrOp; // for ctor 372 BezierQuadTestOp(const SkRect & rect,const SkPMColor4f & color,const GrPathUtils::QuadUVMatrix & devToUV)373 BezierQuadTestOp(const SkRect& rect, const SkPMColor4f& color, 374 const GrPathUtils::QuadUVMatrix& devToUV) 375 : INHERITED(rect, color, ClassID()) 376 , fDevToUV(devToUV) {} 377 378 struct Vertex { 379 SkPoint fPosition; 380 float fKLM[4]; // The last value is ignored. The effect expects a vec4f. 381 }; 382 makeGP(const GrCaps & caps,SkArenaAlloc * arena)383 GrGeometryProcessor* makeGP(const GrCaps& caps, SkArenaAlloc* arena) final { 384 auto tmp = GrQuadEffect::Make(arena, this->color(), SkMatrix::I(), caps, SkMatrix::I(), 385 false); 386 if (!tmp) { 387 return nullptr; 388 } 389 SkASSERT(tmp->vertexStride() == sizeof(Vertex)); 390 return tmp; 391 } 392 onPrepareDraws(GrMeshDrawTarget * target)393 void onPrepareDraws(GrMeshDrawTarget* target) final { 394 QuadHelper helper(target, sizeof(Vertex), 1); 395 Vertex* verts = reinterpret_cast<Vertex*>(helper.vertices()); 396 if (!verts) { 397 return; 398 } 399 SkRect rect = this->rect(); 400 SkPointPriv::SetRectTriStrip(&verts[0].fPosition, rect, sizeof(Vertex)); 401 fDevToUV.apply(verts, 4, sizeof(Vertex), sizeof(SkPoint)); 402 403 fMesh = helper.mesh(); 404 } 405 406 GrPathUtils::QuadUVMatrix fDevToUV; 407 408 inline static constexpr int kVertsPerCubic = 4; 409 inline static constexpr int kIndicesPerCubic = 6; 410 411 using INHERITED = BezierTestOp; 412 }; 413 414 /** 415 * This GM directly exercises effects that draw Bezier quad curves in the GPU backend. 416 */ 417 class BezierQuadEffects : public GpuGM { 418 public: BezierQuadEffects()419 BezierQuadEffects() { 420 this->setBGColor(0xFFFFFFFF); 421 } 422 423 protected: 424 static const int kNumQuads = 5; 425 static const int kCellWidth = 128; 426 static const int kCellHeight = 128; 427 onShortName()428 SkString onShortName() override { 429 return SkString("bezier_quad_effects"); 430 } 431 onISize()432 SkISize onISize() override { 433 return SkISize::Make(kCellWidth, kNumQuads*kCellHeight); 434 } 435 onDraw(GrRecordingContext * rContext,SkCanvas * canvas,SkString * errorMsg)436 DrawResult onDraw(GrRecordingContext* rContext, SkCanvas* canvas, SkString* errorMsg) override { 437 auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas); 438 if (!sdc) { 439 *errorMsg = kErrorMsg_DrawSkippedGpuOnly; 440 return DrawResult::kSkip; 441 } 442 443 const SkScalar w = kCellWidth, h = kCellHeight; 444 const SkPMColor4f kOpaqueBlack = SkPMColor4f::FromBytes_RGBA(0xff000000); 445 446 const SkPoint baseControlPts[kNumQuads][3] = { 447 { { 0.31f * w, 0.01f * h}, { 0.48f * w, 0.74f * h }, { 0.19f * w, 0.33f * h } }, 448 { { 0.00f * w, 0.07f * h}, { 0.30f * w, 0.70f * h }, { 0.47f * w, 0.37f * h } }, 449 { { 0.15f * w, 0.23f * h}, { 0.49f * w, 0.87f * h }, { 0.85f * w, 0.66f * h } }, 450 { { 0.09f * w, 0.15f * h}, { 0.42f * w, 0.33f * h }, { 0.17f * w, 0.38f * h } }, 451 { { 0.98f * w, 0.54f * h}, { 0.83f * w, 0.91f * h }, { 0.62f * w, 0.40f * h } }, 452 }; 453 454 SkPaint ctrlPtPaint; 455 ctrlPtPaint.setColor(SK_ColorRED); 456 457 SkPaint choppedPtPaint; 458 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000); 459 460 SkPaint polyPaint; 461 polyPaint.setColor(0xffA0A0A0); 462 polyPaint.setStrokeWidth(0); 463 polyPaint.setStyle(SkPaint::kStroke_Style); 464 465 SkPaint boundsPaint; 466 boundsPaint.setColor(0xff808080); 467 boundsPaint.setStrokeWidth(0); 468 boundsPaint.setStyle(SkPaint::kStroke_Style); 469 470 for (int row = 0; row < kNumQuads; ++row) { 471 SkScalar x = 0; 472 SkScalar y = row * h; 473 SkPoint controlPts[] = { 474 {x + baseControlPts[row][0].fX, y + baseControlPts[row][0].fY}, 475 {x + baseControlPts[row][1].fX, y + baseControlPts[row][1].fY}, 476 {x + baseControlPts[row][2].fX, y + baseControlPts[row][2].fY} 477 }; 478 479 for (int i = 0; i < 3; ++i) { 480 canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint); 481 } 482 483 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint); 484 485 SkPoint chopped[5]; 486 int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped); 487 488 for (int c = 0; c < cnt; ++c) { 489 SkPoint* pts = chopped + 2 * c; 490 491 for (int i = 0; i < 3; ++i) { 492 canvas->drawCircle(pts[i], 3.f, choppedPtPaint); 493 } 494 495 SkRect bounds; 496 bounds.setBounds(pts, 3); 497 498 canvas->drawRect(bounds, boundsPaint); 499 500 GrPathUtils::QuadUVMatrix DevToUV(pts); 501 502 GrOp::Owner op = BezierQuadTestOp::Make(rContext, bounds, 503 kOpaqueBlack, DevToUV); 504 sdc->addDrawOp(std::move(op)); 505 } 506 } 507 508 return DrawResult::kOk; 509 } 510 511 private: 512 using INHERITED = GM; 513 }; 514 515 DEF_GM(return new BezierConicEffects;) 516 DEF_GM(return new BezierQuadEffects;) 517 } // namespace skiagm 518