/*
 * Copyright 2021 Google LLC.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#ifndef PathStencilCoverOp_DEFINED
#define PathStencilCoverOp_DEFINED

#include "include/core/SkPath.h"
#include "include/core/SkPathTypes.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/private/base/SkDebug.h"
#include "include/private/gpu/ganesh/GrTypesPriv.h"
#include "src/base/SkArenaAlloc.h"
#include "src/core/SkColorData.h"
#include "src/gpu/ganesh/GrBuffer.h"
#include "src/gpu/ganesh/GrCaps.h"
#include "src/gpu/ganesh/GrGpuBuffer.h"
#include "src/gpu/ganesh/GrPaint.h"
#include "src/gpu/ganesh/GrProcessorSet.h"
#include "src/gpu/ganesh/ops/GrDrawOp.h"
#include "src/gpu/ganesh/ops/GrOp.h"
#include "src/gpu/ganesh/tessellate/GrTessellationShader.h"
#include "src/gpu/ganesh/tessellate/PathTessellator.h"

#include <utility>

class GrAppliedClip;
class GrDstProxyView;
class GrOpFlushState;
class GrProgramInfo;
class GrRecordingContext;
class GrSurfaceProxyView;
class SkMatrix;
enum class GrXferBarrierFlags;

namespace skgpu::ganesh {

enum class FillPathFlags;

// Draws paths using a standard Redbook "stencil then cover" method. Curves get linearized by either
// GPU tessellation shaders or indirect draws. This Op doesn't apply analytic AA, so it requires
// MSAA if AA is desired.
class PathStencilCoverOp final : public GrDrawOp {
private:
    DEFINE_OP_CLASS_ID

    using PathDrawList = PathTessellator::PathDrawList;

    // If the path is inverse filled, drawBounds must be the entire backing store dimensions of the
    // render target.
    PathStencilCoverOp(SkArenaAlloc* arena,
                       const SkMatrix& viewMatrix,
                       const SkPath& path,
                       GrPaint&& paint,
                       GrAAType aaType,
                       FillPathFlags pathFlags,
                       const SkRect& drawBounds)
            : GrDrawOp(ClassID())
            , fPathDrawList(arena->make<PathDrawList>(viewMatrix, path, SK_PMColor4fTRANSPARENT))
            , fTotalCombinedPathVerbCnt(path.countVerbs())
            , fPathCount(1)
            , fPathFlags(pathFlags)
            , fAAType(aaType)
            , fColor(paint.getColor4f())
            , fProcessors(std::move(paint)) {
        this->setBounds(drawBounds, HasAABloat::kNo, IsHairline::kNo);
        SkDEBUGCODE(fOriginalDrawBounds = drawBounds;)
    }

    // Constructs a PathStencilCoverOp from an existing draw list.
    // FIXME: The only user of this method is the atlas. We should move the GrProgramInfos into
    // PathTessellator so the atlas can use that directly instead of going through this class.
    PathStencilCoverOp(const PathDrawList* pathDrawList,
                       int totalCombinedVerbCnt,
                       int pathCount,
                       GrPaint&& paint,
                       GrAAType aaType,
                       FillPathFlags pathFlags,
                       const SkRect& drawBounds)
            : GrDrawOp(ClassID())
            , fPathDrawList(pathDrawList)
            , fTotalCombinedPathVerbCnt(totalCombinedVerbCnt)
            , fPathCount(pathCount)
            , fPathFlags(pathFlags)
            , fAAType(aaType)
            , fColor(paint.getColor4f())
            , fProcessors(std::move(paint)) {
        this->setBounds(drawBounds, HasAABloat::kNo, IsHairline::kNo);
        SkDEBUGCODE(fOriginalDrawBounds = drawBounds;)
    }

    const char* name() const override { return "PathStencilCoverOp"; }
    void visitProxies(const GrVisitProxyFunc&) const override;
    FixedFunctionFlags fixedFunctionFlags() const override;
    GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override;

    // All paths in fPathDrawList are required to have the same fill type.
    SkPathFillType pathFillType() const {
        return fPathDrawList->fPath.getFillType();
    }

    // Chooses the rendering method we will use and creates the corresponding tessellator and
    // stencil/cover programs.
    void prePreparePrograms(const GrTessellationShader::ProgramArgs&, GrAppliedClip&& clip);

    void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*,
                      const GrDstProxyView&, GrXferBarrierFlags, GrLoadOp colorLoadOp) override;
    void onPrepare(GrOpFlushState*) override;
    void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;

    const PathDrawList* fPathDrawList;
    const int fTotalCombinedPathVerbCnt;
    const int fPathCount;
    const FillPathFlags fPathFlags;
    const GrAAType fAAType;
    SkPMColor4f fColor;
    GrProcessorSet fProcessors;
    SkDEBUGCODE(SkRect fOriginalDrawBounds;)

    // Decided during prePreparePrograms.
    PathTessellator* fTessellator = nullptr;
    const GrProgramInfo* fStencilFanProgram = nullptr;
    const GrProgramInfo* fStencilPathProgram = nullptr;
    const GrProgramInfo* fCoverBBoxProgram = nullptr;

    // Filled during onPrepare.
    sk_sp<const GrBuffer> fFanBuffer;
    int fFanBaseVertex = 0;
    int fFanVertexCount = 0;

    sk_sp<const GrBuffer> fBBoxBuffer;
    int fBBoxBaseInstance = 0;

    // Only used if sk_VertexID is not supported.
    sk_sp<const GrGpuBuffer> fBBoxVertexBufferIfNoIDSupport;

    friend class GrOp;  // For ctor.
};

}  // namespace skgpu::ganesh

#endif // PathStencilCoverOp_DEFINED
