| /* |
| * Copyright 2013 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| // This test only works with the GPU backend. |
| |
| #include "gm/gm.h" |
| #include "include/core/SkBlendMode.h" |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkMatrix.h" |
| #include "include/core/SkPaint.h" |
| #include "include/core/SkPoint.h" |
| #include "include/core/SkPoint3.h" |
| #include "include/core/SkRect.h" |
| #include "include/core/SkRefCnt.h" |
| #include "include/core/SkScalar.h" |
| #include "include/core/SkSize.h" |
| #include "include/core/SkString.h" |
| #include "include/core/SkTypes.h" |
| #include "include/gpu/GrRecordingContext.h" |
| #include "include/private/GrSharedEnums.h" |
| #include "include/private/GrTypesPriv.h" |
| #include "include/private/SkColorData.h" |
| #include "include/utils/SkRandom.h" |
| #include "src/core/SkGeometry.h" |
| #include "src/core/SkPointPriv.h" |
| #include "src/gpu/GrCaps.h" |
| #include "src/gpu/GrDirectContextPriv.h" |
| #include "src/gpu/GrGeometryProcessor.h" |
| #include "src/gpu/GrMemoryPool.h" |
| #include "src/gpu/GrOpFlushState.h" |
| #include "src/gpu/GrOpsRenderPass.h" |
| #include "src/gpu/GrPaint.h" |
| #include "src/gpu/GrProcessorAnalysis.h" |
| #include "src/gpu/GrProcessorSet.h" |
| #include "src/gpu/GrProgramInfo.h" |
| #include "src/gpu/GrRecordingContextPriv.h" |
| #include "src/gpu/GrSurfaceDrawContext.h" |
| #include "src/gpu/GrUserStencilSettings.h" |
| #include "src/gpu/effects/GrBezierEffect.h" |
| #include "src/gpu/effects/GrPorterDuffXferProcessor.h" |
| #include "src/gpu/geometry/GrPathUtils.h" |
| #include "src/gpu/ops/GrDrawOp.h" |
| #include "src/gpu/ops/GrMeshDrawOp.h" |
| #include "src/gpu/ops/GrOp.h" |
| #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| class GrAppliedClip; |
| |
| namespace skiagm { |
| |
| class BezierTestOp : public GrMeshDrawOp { |
| public: |
| FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } |
| |
| GrProcessorSet::Analysis finalize( |
| const GrCaps& caps, const GrAppliedClip* clip, GrClampType clampType) override { |
| return fProcessorSet.finalize( |
| fColor, GrProcessorAnalysisCoverage::kSingleChannel, clip, |
| &GrUserStencilSettings::kUnused, caps, clampType, &fColor); |
| } |
| |
| void visitProxies(const GrVisitProxyFunc& func) const override { |
| if (fProgramInfo) { |
| fProgramInfo->visitFPProxies(func); |
| } else { |
| fProcessorSet.visitProxies(func); |
| } |
| } |
| |
| protected: |
| BezierTestOp(const SkRect& rect, const SkPMColor4f& color, int32_t classID) |
| : INHERITED(classID) |
| , fRect(rect) |
| , fColor(color) |
| , fProcessorSet(SkBlendMode::kSrc) { |
| this->setBounds(rect, HasAABloat::kYes, IsHairline::kNo); |
| } |
| |
| virtual GrGeometryProcessor* makeGP(const GrCaps& caps, SkArenaAlloc* arena) = 0; |
| |
| GrProgramInfo* programInfo() override { return fProgramInfo; } |
| |
| void onCreateProgramInfo(const GrCaps* caps, |
| SkArenaAlloc* arena, |
| const GrSurfaceProxyView& writeView, |
| GrAppliedClip&& appliedClip, |
| const GrDstProxyView& dstProxyView, |
| GrXferBarrierFlags renderPassXferBarriers, |
| GrLoadOp colorLoadOp) override { |
| auto gp = this->makeGP(*caps, arena); |
| if (!gp) { |
| return; |
| } |
| |
| GrPipeline::InputFlags flags = GrPipeline::InputFlags::kNone; |
| |
| fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView, |
| std::move(appliedClip), |
| dstProxyView, gp, |
| std::move(fProcessorSet), |
| GrPrimitiveType::kTriangles, |
| renderPassXferBarriers, |
| colorLoadOp, |
| flags); |
| } |
| |
| void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) final { |
| if (!fProgramInfo) { |
| this->createProgramInfo(flushState); |
| } |
| |
| if (!fProgramInfo) { |
| return; |
| } |
| |
| flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds); |
| flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline()); |
| flushState->drawMesh(*fMesh); |
| } |
| |
| const SkRect& rect() const { return fRect; } |
| const SkPMColor4f& color() const { return fColor; } |
| |
| protected: |
| GrSimpleMesh* fMesh = nullptr; // filled in by the derived classes |
| |
| private: |
| SkRect fRect; |
| SkPMColor4f fColor; |
| GrProcessorSet fProcessorSet; |
| GrProgramInfo* fProgramInfo = nullptr; |
| |
| using INHERITED = GrMeshDrawOp; |
| }; |
| |
| /** |
| * This GM directly exercises effects that draw Bezier curves in the GPU backend. |
| */ |
| class BezierConicTestOp : public BezierTestOp { |
| public: |
| DEFINE_OP_CLASS_ID |
| |
| const char* name() const final { return "BezierConicTestOp"; } |
| |
| static GrOp::Owner Make(GrRecordingContext* context, |
| const SkRect& rect, |
| const SkPMColor4f& color, |
| const SkMatrix& klm) { |
| return GrOp::Make<BezierConicTestOp>(context, rect, color, klm); |
| } |
| |
| private: |
| friend class ::GrOp; // for ctor |
| |
| BezierConicTestOp(const SkRect& rect, const SkPMColor4f& color, const SkMatrix& klm) |
| : INHERITED(rect, color, ClassID()) |
| , fKLM(klm) {} |
| |
| struct Vertex { |
| SkPoint fPosition; |
| float fKLM[4]; // The last value is ignored. The effect expects a vec4f. |
| }; |
| |
| GrGeometryProcessor* makeGP(const GrCaps& caps, SkArenaAlloc* arena) final { |
| auto tmp = GrConicEffect::Make(arena, this->color(), SkMatrix::I(), caps, SkMatrix::I(), |
| false); |
| if (!tmp) { |
| return nullptr; |
| } |
| SkASSERT(tmp->vertexStride() == sizeof(Vertex)); |
| return tmp; |
| } |
| |
| void onPrepareDraws(Target* target) final { |
| QuadHelper helper(target, sizeof(Vertex), 1); |
| Vertex* verts = reinterpret_cast<Vertex*>(helper.vertices()); |
| if (!verts) { |
| return; |
| } |
| SkRect rect = this->rect(); |
| SkPointPriv::SetRectTriStrip(&verts[0].fPosition, rect, sizeof(Vertex)); |
| for (int v = 0; v < 4; ++v) { |
| SkPoint3 pt3 = {verts[v].fPosition.x(), verts[v].fPosition.y(), 1.f}; |
| fKLM.mapHomogeneousPoints((SkPoint3* ) verts[v].fKLM, &pt3, 1); |
| } |
| |
| fMesh = helper.mesh(); |
| } |
| |
| SkMatrix fKLM; |
| |
| static constexpr int kVertsPerCubic = 4; |
| static constexpr int kIndicesPerCubic = 6; |
| |
| using INHERITED = BezierTestOp; |
| }; |
| |
| |
| /** |
| * This GM directly exercises effects that draw Bezier curves in the GPU backend. |
| */ |
| class BezierConicEffects : public GpuGM { |
| public: |
| BezierConicEffects() { |
| this->setBGColor(0xFFFFFFFF); |
| } |
| |
| protected: |
| static const int kNumConics = 10; |
| static const int kCellWidth = 128; |
| static const int kCellHeight = 128; |
| |
| SkString onShortName() override { |
| return SkString("bezier_conic_effects"); |
| } |
| |
| SkISize onISize() override { |
| return SkISize::Make(kCellWidth, kNumConics*kCellHeight); |
| } |
| |
| void onDraw(GrRecordingContext* context, GrSurfaceDrawContext* surfaceDrawContext, |
| SkCanvas* canvas) override { |
| |
| const SkScalar w = kCellWidth, h = kCellHeight; |
| const SkPMColor4f kOpaqueBlack = SkPMColor4f::FromBytes_RGBA(0xff000000); |
| |
| const SkPoint baseControlPts[kNumConics][3] = { |
| { { 0.31f * w, 0.01f * h}, { 0.48f * w, 0.74f * h }, { 0.19f * w, 0.33f * h } }, |
| { { 0.00f * w, 0.07f * h}, { 0.30f * w, 0.70f * h }, { 0.47f * w, 0.37f * h } }, |
| { { 0.15f * w, 0.23f * h}, { 0.49f * w, 0.87f * h }, { 0.85f * w, 0.66f * h } }, |
| { { 0.09f * w, 0.15f * h}, { 0.42f * w, 0.33f * h }, { 0.17f * w, 0.38f * h } }, |
| { { 0.98f * w, 0.54f * h}, { 0.83f * w, 0.91f * h }, { 0.62f * w, 0.40f * h } }, |
| { { 0.96f * w, 0.65f * h}, { 0.03f * w, 0.79f * h }, { 0.24f * w, 0.56f * h } }, |
| { { 0.57f * w, 0.12f * h}, { 0.33f * w, 0.67f * h }, { 0.59f * w, 0.33f * h } }, |
| { { 0.12f * w, 0.72f * h}, { 0.69f * w, 0.85f * h }, { 0.46f * w, 0.32f * h } }, |
| { { 0.27f * w, 0.49f * h}, { 0.41f * w, 0.02f * h }, { 0.11f * w, 0.42f * h } }, |
| { { 0.40f * w, 0.13f * h}, { 0.83f * w, 0.30f * h }, { 0.31f * w, 0.68f * h } }, |
| }; |
| const SkScalar weights[kNumConics] = { 0.62f, 0.01f, 0.95f, 1.48f, 0.37f, |
| 0.66f, 0.15f, 0.14f, 0.61f, 1.4f }; |
| |
| SkPaint ctrlPtPaint; |
| ctrlPtPaint.setColor(SK_ColorRED); |
| |
| SkPaint choppedPtPaint; |
| choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000); |
| |
| SkPaint polyPaint; |
| polyPaint.setColor(0xffA0A0A0); |
| polyPaint.setStrokeWidth(0); |
| polyPaint.setStyle(SkPaint::kStroke_Style); |
| |
| SkPaint boundsPaint; |
| boundsPaint.setColor(0xff808080); |
| boundsPaint.setStrokeWidth(0); |
| boundsPaint.setStyle(SkPaint::kStroke_Style); |
| |
| |
| for (int row = 0; row < kNumConics; ++row) { |
| SkScalar x = 0; |
| SkScalar y = row * h; |
| SkPoint controlPts[] = { |
| {x + baseControlPts[row][0].fX, y + baseControlPts[row][0].fY}, |
| {x + baseControlPts[row][1].fX, y + baseControlPts[row][1].fY}, |
| {x + baseControlPts[row][2].fX, y + baseControlPts[row][2].fY} |
| }; |
| |
| for (int i = 0; i < 3; ++i) { |
| canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint); |
| } |
| |
| canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint); |
| |
| SkConic dst[4]; |
| SkMatrix klm; |
| int cnt = ChopConic(controlPts, dst, weights[row]); |
| GrPathUtils::getConicKLM(controlPts, weights[row], &klm); |
| |
| for (int c = 0; c < cnt; ++c) { |
| SkPoint* pts = dst[c].fPts; |
| for (int i = 0; i < 3; ++i) { |
| canvas->drawCircle(pts[i], 3.f, choppedPtPaint); |
| } |
| |
| SkRect bounds; |
| bounds.setBounds(pts, 3); |
| |
| canvas->drawRect(bounds, boundsPaint); |
| |
| GrOp::Owner op = BezierConicTestOp::Make(context, bounds, |
| kOpaqueBlack, klm); |
| surfaceDrawContext->addDrawOp(std::move(op)); |
| } |
| } |
| } |
| |
| private: |
| // Uses the max curvature function for quads to estimate |
| // where to chop the conic. If the max curvature is not |
| // found along the curve segment it will return 1 and |
| // dst[0] is the original conic. If it returns 2 the dst[0] |
| // and dst[1] are the two new conics. |
| static int SplitConic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) { |
| SkScalar t = SkFindQuadMaxCurvature(src); |
| if (t == 0 || t == 1) { |
| if (dst) { |
| dst[0].set(src, weight); |
| } |
| return 1; |
| } else { |
| if (dst) { |
| SkConic conic; |
| conic.set(src, weight); |
| if (!conic.chopAt(t, dst)) { |
| dst[0].set(src, weight); |
| return 1; |
| } |
| } |
| return 2; |
| } |
| } |
| |
| // Calls SplitConic on the entire conic and then once more on each subsection. |
| // Most cases will result in either 1 conic (chop point is not within t range) |
| // or 3 points (split once and then one subsection is split again). |
| static int ChopConic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) { |
| SkConic dstTemp[2]; |
| int conicCnt = SplitConic(src, dstTemp, weight); |
| if (2 == conicCnt) { |
| int conicCnt2 = SplitConic(dstTemp[0].fPts, dst, dstTemp[0].fW); |
| conicCnt = conicCnt2 + SplitConic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW); |
| } else { |
| dst[0] = dstTemp[0]; |
| } |
| return conicCnt; |
| } |
| |
| using INHERITED = GM; |
| }; |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| class BezierQuadTestOp : public BezierTestOp { |
| public: |
| DEFINE_OP_CLASS_ID |
| const char* name() const override { return "BezierQuadTestOp"; } |
| |
| static GrOp::Owner Make(GrRecordingContext* context, |
| const SkRect& rect, |
| const SkPMColor4f& color, |
| const GrPathUtils::QuadUVMatrix& devToUV) { |
| return GrOp::Make<BezierQuadTestOp>(context, rect, color, devToUV); |
| } |
| |
| private: |
| friend class ::GrOp; // for ctor |
| |
| BezierQuadTestOp(const SkRect& rect, const SkPMColor4f& color, |
| const GrPathUtils::QuadUVMatrix& devToUV) |
| : INHERITED(rect, color, ClassID()) |
| , fDevToUV(devToUV) {} |
| |
| struct Vertex { |
| SkPoint fPosition; |
| float fKLM[4]; // The last value is ignored. The effect expects a vec4f. |
| }; |
| |
| GrGeometryProcessor* makeGP(const GrCaps& caps, SkArenaAlloc* arena) final { |
| auto tmp = GrQuadEffect::Make(arena, this->color(), SkMatrix::I(), caps, SkMatrix::I(), |
| false); |
| if (!tmp) { |
| return nullptr; |
| } |
| SkASSERT(tmp->vertexStride() == sizeof(Vertex)); |
| return tmp; |
| } |
| |
| void onPrepareDraws(Target* target) final { |
| QuadHelper helper(target, sizeof(Vertex), 1); |
| Vertex* verts = reinterpret_cast<Vertex*>(helper.vertices()); |
| if (!verts) { |
| return; |
| } |
| SkRect rect = this->rect(); |
| SkPointPriv::SetRectTriStrip(&verts[0].fPosition, rect, sizeof(Vertex)); |
| fDevToUV.apply(verts, 4, sizeof(Vertex), sizeof(SkPoint)); |
| |
| fMesh = helper.mesh(); |
| } |
| |
| GrPathUtils::QuadUVMatrix fDevToUV; |
| |
| static constexpr int kVertsPerCubic = 4; |
| static constexpr int kIndicesPerCubic = 6; |
| |
| using INHERITED = BezierTestOp; |
| }; |
| |
| /** |
| * This GM directly exercises effects that draw Bezier quad curves in the GPU backend. |
| */ |
| class BezierQuadEffects : public GpuGM { |
| public: |
| BezierQuadEffects() { |
| this->setBGColor(0xFFFFFFFF); |
| } |
| |
| protected: |
| static const int kNumQuads = 5; |
| static const int kCellWidth = 128; |
| static const int kCellHeight = 128; |
| |
| SkString onShortName() override { |
| return SkString("bezier_quad_effects"); |
| } |
| |
| SkISize onISize() override { |
| return SkISize::Make(kCellWidth, kNumQuads*kCellHeight); |
| } |
| |
| void onDraw(GrRecordingContext* context, GrSurfaceDrawContext* surfaceDrawContext, |
| SkCanvas* canvas) override { |
| |
| const SkScalar w = kCellWidth, h = kCellHeight; |
| const SkPMColor4f kOpaqueBlack = SkPMColor4f::FromBytes_RGBA(0xff000000); |
| |
| const SkPoint baseControlPts[kNumQuads][3] = { |
| { { 0.31f * w, 0.01f * h}, { 0.48f * w, 0.74f * h }, { 0.19f * w, 0.33f * h } }, |
| { { 0.00f * w, 0.07f * h}, { 0.30f * w, 0.70f * h }, { 0.47f * w, 0.37f * h } }, |
| { { 0.15f * w, 0.23f * h}, { 0.49f * w, 0.87f * h }, { 0.85f * w, 0.66f * h } }, |
| { { 0.09f * w, 0.15f * h}, { 0.42f * w, 0.33f * h }, { 0.17f * w, 0.38f * h } }, |
| { { 0.98f * w, 0.54f * h}, { 0.83f * w, 0.91f * h }, { 0.62f * w, 0.40f * h } }, |
| }; |
| |
| SkPaint ctrlPtPaint; |
| ctrlPtPaint.setColor(SK_ColorRED); |
| |
| SkPaint choppedPtPaint; |
| choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000); |
| |
| SkPaint polyPaint; |
| polyPaint.setColor(0xffA0A0A0); |
| polyPaint.setStrokeWidth(0); |
| polyPaint.setStyle(SkPaint::kStroke_Style); |
| |
| SkPaint boundsPaint; |
| boundsPaint.setColor(0xff808080); |
| boundsPaint.setStrokeWidth(0); |
| boundsPaint.setStyle(SkPaint::kStroke_Style); |
| |
| for (int row = 0; row < kNumQuads; ++row) { |
| SkScalar x = 0; |
| SkScalar y = row * h; |
| SkPoint controlPts[] = { |
| {x + baseControlPts[row][0].fX, y + baseControlPts[row][0].fY}, |
| {x + baseControlPts[row][1].fX, y + baseControlPts[row][1].fY}, |
| {x + baseControlPts[row][2].fX, y + baseControlPts[row][2].fY} |
| }; |
| |
| for (int i = 0; i < 3; ++i) { |
| canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint); |
| } |
| |
| canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint); |
| |
| SkPoint chopped[5]; |
| int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped); |
| |
| for (int c = 0; c < cnt; ++c) { |
| SkPoint* pts = chopped + 2 * c; |
| |
| for (int i = 0; i < 3; ++i) { |
| canvas->drawCircle(pts[i], 3.f, choppedPtPaint); |
| } |
| |
| SkRect bounds; |
| bounds.setBounds(pts, 3); |
| |
| canvas->drawRect(bounds, boundsPaint); |
| |
| GrPathUtils::QuadUVMatrix DevToUV(pts); |
| |
| GrOp::Owner op = BezierQuadTestOp::Make(context, bounds, |
| kOpaqueBlack, DevToUV); |
| surfaceDrawContext->addDrawOp(std::move(op)); |
| } |
| } |
| } |
| |
| private: |
| using INHERITED = GM; |
| }; |
| |
| DEF_GM(return new BezierConicEffects;) |
| DEF_GM(return new BezierQuadEffects;) |
| } // namespace skiagm |