/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrDrawPathOp.h"
#include "GrAppliedClip.h"
#include "GrMemoryPool.h"
#include "GrRecordingContext.h"
#include "GrRecordingContextPriv.h"
#include "GrRenderTargetContext.h"
#include "GrRenderTargetPriv.h"
#include "SkTemplates.h"

static constexpr GrUserStencilSettings kCoverPass{
        GrUserStencilSettings::StaticInit<
                0x0000,
                GrUserStencilTest::kNotEqual,
                0xffff,
                GrUserStencilOp::kZero,
                GrUserStencilOp::kKeep,
                0xffff>()
};

GrDrawPathOpBase::GrDrawPathOpBase(uint32_t classID, const SkMatrix& viewMatrix, GrPaint&& paint,
                                   GrPathRendering::FillType fill, GrAAType aaType)
        : INHERITED(classID)
        , fViewMatrix(viewMatrix)
        , fInputColor(paint.getColor4f())
        , fFillType(fill)
        , fAAType(aaType)
        , fProcessorSet(std::move(paint)) {}

#ifdef SK_DEBUG
SkString GrDrawPathOp::dumpInfo() const {
    SkString string;
    string.printf("PATH: 0x%p", fPath.get());
    string.append(INHERITED::dumpInfo());
    return string;
}
#endif

GrPipeline::InitArgs GrDrawPathOpBase::pipelineInitArgs(const GrOpFlushState& state) {
    GrPipeline::InitArgs args;
    if (GrAATypeIsHW(fAAType)) {
        args.fFlags |= GrPipeline::kHWAntialias_Flag;
    }
    args.fUserStencil = &kCoverPass;
    args.fCaps = &state.caps();
    args.fResourceProvider = state.resourceProvider();
    args.fDstProxy = state.drawOpArgs().fDstProxy;
    return args;
}

const GrProcessorSet::Analysis& GrDrawPathOpBase::doProcessorAnalysis(
        const GrCaps& caps, const GrAppliedClip* clip, GrFSAAType fsaaType,
        GrClampType clampType) {
    fAnalysis = fProcessorSet.finalize(
            fInputColor, GrProcessorAnalysisCoverage::kNone, clip, &kCoverPass, fsaaType, caps,
            clampType, &fInputColor);
    return fAnalysis;
}

//////////////////////////////////////////////////////////////////////////////

void init_stencil_pass_settings(const GrOpFlushState& flushState,
                                GrPathRendering::FillType fillType, GrStencilSettings* stencil) {
    const GrAppliedClip* appliedClip = flushState.drawOpArgs().fAppliedClip;
    bool stencilClip = appliedClip && appliedClip->hasStencilClip();
    stencil->reset(GrPathRendering::GetStencilPassSettings(fillType), stencilClip,
                   flushState.drawOpArgs().renderTarget()->renderTargetPriv().numStencilBits());
}

//////////////////////////////////////////////////////////////////////////////

std::unique_ptr<GrDrawOp> GrDrawPathOp::Make(GrRecordingContext* context,
                                             const SkMatrix& viewMatrix,
                                             GrPaint&& paint,
                                             GrAAType aaType,
                                             GrPath* path) {
    GrOpMemoryPool* pool = context->priv().opMemoryPool();

    return pool->allocate<GrDrawPathOp>(viewMatrix, std::move(paint), aaType, path);
}

void GrDrawPathOp::onExecute(GrOpFlushState* state, const SkRect& chainBounds) {
    GrAppliedClip appliedClip = state->detachAppliedClip();
    GrPipeline::FixedDynamicState fixedDynamicState(appliedClip.scissorState().rect());
    GrPipeline pipeline(this->pipelineInitArgs(*state), this->detachProcessors(),
                        std::move(appliedClip));
    sk_sp<GrPathProcessor> pathProc(GrPathProcessor::Create(this->color(), this->viewMatrix()));

    GrStencilSettings stencil;
    init_stencil_pass_settings(*state, this->fillType(), &stencil);
    state->gpu()->pathRendering()->drawPath(state->drawOpArgs().renderTarget(),
                                            state->drawOpArgs().origin(),
                                            *pathProc, pipeline, fixedDynamicState, stencil,
                                            fPath.get());
}

//////////////////////////////////////////////////////////////////////////////

inline void pre_translate_transform_values(const float* xforms,
                                           GrPathRendering::PathTransformType type, int count,
                                           SkScalar x, SkScalar y, float* dst) {
    if (0 == x && 0 == y) {
        memcpy(dst, xforms, count * GrPathRendering::PathTransformSize(type) * sizeof(float));
        return;
    }
    switch (type) {
        case GrPathRendering::kNone_PathTransformType:
            SK_ABORT("Cannot pre-translate kNone_PathTransformType.");
            break;
        case GrPathRendering::kTranslateX_PathTransformType:
            SkASSERT(0 == y);
            for (int i = 0; i < count; i++) {
                dst[i] = xforms[i] + x;
            }
            break;
        case GrPathRendering::kTranslateY_PathTransformType:
            SkASSERT(0 == x);
            for (int i = 0; i < count; i++) {
                dst[i] = xforms[i] + y;
            }
            break;
        case GrPathRendering::kTranslate_PathTransformType:
            for (int i = 0; i < 2 * count; i += 2) {
                dst[i] = xforms[i] + x;
                dst[i + 1] = xforms[i + 1] + y;
            }
            break;
        case GrPathRendering::kAffine_PathTransformType:
            for (int i = 0; i < 6 * count; i += 6) {
                dst[i] = xforms[i];
                dst[i + 1] = xforms[i + 1];
                dst[i + 2] = xforms[i] * x + xforms[i + 1] * y + xforms[i + 2];
                dst[i + 3] = xforms[i + 3];
                dst[i + 4] = xforms[i + 4];
                dst[i + 5] = xforms[i + 3] * x + xforms[i + 4] * y + xforms[i + 5];
            }
            break;
        default:
            SK_ABORT("Unknown transform type.");
            break;
    }
}