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

#ifndef GrRenderTargetContext_DEFINED
#define GrRenderTargetContext_DEFINED

#include "GrColor.h"
#include "GrContext.h"
#include "GrPaint.h"
#include "GrRenderTarget.h"
#include "SkRefCnt.h"
#include "SkRegion.h"
#include "SkSurfaceProps.h"
#include "../private/GrInstancedPipelineInfo.h"
#include "../private/GrRenderTargetProxy.h"
#include "../private/GrSingleOwner.h"

class GrAuditTrail;
class GrClip;
class GrDrawBatch;
class GrRenderTargetContextPriv;
class GrDrawPathBatchBase;
class GrDrawingManager;
class GrFixedClip;
class GrPaint;
class GrPathProcessor;
class GrPipelineBuilder;
class GrRenderTarget;
class GrRenderTargetOpList;
class GrStyle;
class GrSurface;
class GrTextureProxy;
struct GrUserStencilSettings;
class SkDrawFilter;
struct SkIPoint;
struct SkIRect;
class SkLatticeIter;
class SkMatrix;
class SkPaint;
class SkPath;
struct SkPoint;
struct SkRect;
class SkRRect;
struct SkRSXform;
class SkTextBlob;

/*
 * A helper object to orchestrate draws
 */
class SK_API GrRenderTargetContext : public SkRefCnt {
public:
    ~GrRenderTargetContext() override;

    bool copySurface(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint);

    // TODO: it is odd that we need both the SkPaint in the following 3 methods.
    // We should extract the text parameters from SkPaint and pass them separately
    // akin to GrStyle (GrTextInfo?)
    virtual void drawText(const GrClip&,  const GrPaint&, const SkPaint&,
                          const SkMatrix& viewMatrix, const char text[], size_t byteLength,
                          SkScalar x, SkScalar y, const SkIRect& clipBounds);
    virtual void drawPosText(const GrClip&, const GrPaint&, const SkPaint&,
                             const SkMatrix& viewMatrix, const char text[], size_t byteLength,
                             const SkScalar pos[], int scalarsPerPosition,
                             const SkPoint& offset, const SkIRect& clipBounds);
    virtual void drawTextBlob(const GrClip&, const SkPaint&,
                              const SkMatrix& viewMatrix, const SkTextBlob*,
                              SkScalar x, SkScalar y,
                              SkDrawFilter*, const SkIRect& clipBounds);

    /**
     * Provides a perfomance hint that the render target's contents are allowed
     * to become undefined.
     */
    void discard();

    /**
     * Clear the entire or rect of the render target, ignoring any clips.
     * @param rect  the rect to clear or the whole thing if rect is NULL.
     * @param color the color to clear to.
     * @param canIgnoreRect allows partial clears to be converted to whole
     *                      clears on platforms for which that is cheap
     */
    void clear(const SkIRect* rect, GrColor color, bool canIgnoreRect);

    /**
     *  Draw everywhere (respecting the clip) with the paint.
     */
    void drawPaint(const GrClip&, const GrPaint&, const SkMatrix& viewMatrix);

    /**
     *  Draw the rect using a paint.
     *  @param paint        describes how to color pixels.
     *  @param viewMatrix   transformation matrix
     *  @param style        The style to apply. Null means fill. Currently path effects are not
     *                      allowed.
     *  The rects coords are used to access the paint (through texture matrix)
     */
    void drawRect(const GrClip&,
                  const GrPaint& paint,
                  const SkMatrix& viewMatrix,
                  const SkRect&,
                  const GrStyle* style  = nullptr);

    /**
     * Maps a rectangle of shader coordinates to a rectangle and fills that rectangle.
     *
     * @param paint         describes how to color pixels.
     * @param viewMatrix    transformation matrix which applies to rectToDraw
     * @param rectToDraw    the rectangle to draw
     * @param localRect     the rectangle of shader coordinates applied to rectToDraw
     */
    void fillRectToRect(const GrClip&,
                        const GrPaint& paint,
                        const SkMatrix& viewMatrix,
                        const SkRect& rectToDraw,
                        const SkRect& localRect);

    /**
     * Fills a rect with a paint and a localMatrix.
     */
    void fillRectWithLocalMatrix(const GrClip& clip,
                                 const GrPaint& paint,
                                 const SkMatrix& viewMatrix,
                                 const SkRect& rect,
                                 const SkMatrix& localMatrix);

    /**
     *  Draw a roundrect using a paint.
     *
     *  @param paint        describes how to color pixels.
     *  @param viewMatrix   transformation matrix
     *  @param rrect        the roundrect to draw
     *  @param style        style to apply to the rrect. Currently path effects are not allowed.
     */
    void drawRRect(const GrClip&,
                   const GrPaint&,
                   const SkMatrix& viewMatrix,
                   const SkRRect& rrect,
                   const GrStyle& style);

    /**
     *  Shortcut for drawing an SkPath consisting of nested rrects using a paint.
     *  Does not support stroking. The result is undefined if outer does not contain
     *  inner.
     *
     *  @param paint        describes how to color pixels.
     *  @param viewMatrix   transformation matrix
     *  @param outer        the outer roundrect
     *  @param inner        the inner roundrect
     */
    void drawDRRect(const GrClip&,
                    const GrPaint&,
                    const SkMatrix& viewMatrix,
                    const SkRRect& outer,
                    const SkRRect& inner);

    /**
     * Draws a path.
     *
     * @param paint         describes how to color pixels.
     * @param viewMatrix    transformation matrix
     * @param path          the path to draw
     * @param style         style to apply to the path.
     */
    void drawPath(const GrClip&,
                  const GrPaint&,
                  const SkMatrix& viewMatrix,
                  const SkPath&,
                  const GrStyle& style);

    /**
     * Draws vertices with a paint.
     *
     * @param   paint           describes how to color pixels.
     * @param   viewMatrix      transformation matrix
     * @param   primitiveType   primitives type to draw.
     * @param   vertexCount     number of vertices.
     * @param   positions       array of vertex positions, required.
     * @param   texCoords       optional array of texture coordinates used
     *                          to access the paint.
     * @param   colors          optional array of per-vertex colors, supercedes
     *                          the paint's color field.
     * @param   indices         optional array of indices. If NULL vertices
     *                          are drawn non-indexed.
     * @param   indexCount      if indices is non-null then this is the
     *                          number of indices.
     */
    void drawVertices(const GrClip&,
                      const GrPaint& paint,
                      const SkMatrix& viewMatrix,
                      GrPrimitiveType primitiveType,
                      int vertexCount,
                      const SkPoint positions[],
                      const SkPoint texs[],
                      const GrColor colors[],
                      const uint16_t indices[],
                      int indexCount);

    /**
     * Draws textured sprites from an atlas with a paint.
     *
     * @param   paint           describes how to color pixels.
     * @param   viewMatrix      transformation matrix
     * @param   spriteCount     number of sprites.
     * @param   xform           array of compressed transformation data, required.
     * @param   texRect         array of texture rectangles used to access the paint.
     * @param   colors          optional array of per-sprite colors, supercedes
     *                          the paint's color field.
     */
    void drawAtlas(const GrClip&,
                   const GrPaint& paint,
                   const SkMatrix& viewMatrix,
                   int spriteCount,
                   const SkRSXform xform[],
                   const SkRect texRect[],
                   const SkColor colors[]);

    /**
     * Draws a region.
     *
     * @param paint         describes how to color pixels
     * @param viewMatrix    transformation matrix
     * @param region        the region to be drawn
     * @param style         style to apply to the region
     */
    void drawRegion(const GrClip&,
                    const GrPaint& paint,
                    const SkMatrix& viewMatrix,
                    const SkRegion& region,
                    const GrStyle& style);

    /**
     * Draws an oval.
     *
     * @param paint         describes how to color pixels.
     * @param viewMatrix    transformation matrix
     * @param oval          the bounding rect of the oval.
     * @param style         style to apply to the oval. Currently path effects are not allowed.
     */
    void drawOval(const GrClip&,
                  const GrPaint& paint,
                  const SkMatrix& viewMatrix,
                  const SkRect& oval,
                  const GrStyle& style);
   /**
    * Draws a partial arc of an oval.
    *
    * @param paint         describes how to color pixels.
    * @param viewMatrix    transformation matrix.
    * @param oval          the bounding rect of the oval.
    * @param startAngle    starting angle in degrees.
    * @param sweepAngle    angle to sweep in degrees. Must be in (-360, 360)
    * @param useCenter     true means that the implied path begins at the oval center, connects as a
    *                      line to the point indicated by the start contains the arc indicated by
    *                      the sweep angle. If false the line beginning at the center point is
    *                      omitted.
    * @param style         style to apply to the oval.
    */
    void drawArc(const GrClip&,
                 const GrPaint& paint,
                 const SkMatrix& viewMatrix,
                 const SkRect& oval,
                 SkScalar startAngle,
                 SkScalar sweepAngle,
                 bool useCenter,
                 const GrStyle& style);

    /**
     * Draw the image as a set of rects, specified by |iter|.
     */
    void drawImageLattice(const GrClip&,
                          const GrPaint& paint,
                          const SkMatrix& viewMatrix,
                          int imageWidth,
                          int imageHeight,
                          std::unique_ptr<SkLatticeIter> iter,
                          const SkRect& dst);

    /**
     * After this returns any pending surface IO will be issued to the backend 3D API and
     * if the surface has MSAA it will be resolved.
     */
    void prepareForExternalIO();

    /**
     * Reads a rectangle of pixels from the render target context.
     * @param dstInfo       image info for the destination
     * @param dstBuffer     destination pixels for the read
     * @param dstRowBytes   bytes in a row of 'dstBuffer'
     * @param x             x offset w/in the render target context from which to read
     * @param y             y offset w/in the render target context from which to read
     *
     * @return true if the read succeeded, false if not. The read can fail because of an
     *              unsupported pixel config.
     */
    bool readPixels(const SkImageInfo& dstInfo, void* dstBuffer, size_t dstRowBytes, int x, int y);

    /**
     * Writes a rectangle of pixels [srcInfo, srcBuffer, srcRowbytes] into the 
     * renderTargetContext at the specified position.
     * @param srcInfo       image info for the source pixels
     * @param srcBuffer     source for the write
     * @param srcRowBytes   bytes in a row of 'srcBuffer'
     * @param x             x offset w/in the render target context at which to write
     * @param y             y offset w/in the render target context at which to write
     *
     * @return true if the write succeeded, false if not. The write can fail because of an
     *              unsupported pixel config.
     */
    bool writePixels(const SkImageInfo& srcInfo, const void* srcBuffer, size_t srcRowBytes,
                     int x, int y);

    bool isStencilBufferMultisampled() const {
        return fRenderTargetProxy->isStencilBufferMultisampled();
    }
    bool isUnifiedMultisampled() const { return fRenderTargetProxy->isUnifiedMultisampled(); }
    bool hasMixedSamples() const { return fRenderTargetProxy->isMixedSampled(); }

    bool mustUseHWAA(const GrPaint& paint) const {
        return paint.isAntiAlias() && fRenderTargetProxy->isUnifiedMultisampled();
    }

    const GrCaps* caps() const { return fContext->caps(); }
    const GrSurfaceDesc& desc() const { return fRenderTargetProxy->desc(); }
    int width() const { return fRenderTargetProxy->width(); }
    int height() const { return fRenderTargetProxy->height(); }
    GrPixelConfig config() const { return fRenderTargetProxy->config(); }
    int numColorSamples() const { return fRenderTargetProxy->numColorSamples(); }
    bool isGammaCorrect() const { return SkToBool(fColorSpace.get()); }
    SkDestinationSurfaceColorMode colorMode() const {
        return this->isGammaCorrect() ? SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware
                                      : SkDestinationSurfaceColorMode::kLegacy;
    }
    const SkSurfaceProps& surfaceProps() const { return fSurfaceProps; }
    SkColorSpace* getColorSpace() const { return fColorSpace.get(); }
    GrColorSpaceXform* getColorXformFromSRGB() const { return fColorXformFromSRGB.get(); }
    GrSurfaceOrigin origin() const { return fRenderTargetProxy->origin(); }

    bool wasAbandoned() const;

    GrRenderTarget* instantiate();

    GrRenderTarget* accessRenderTarget() {
        // TODO: usage of this entry point needs to be reduced and potentially eliminated
        // since it ends the deferral of the GrRenderTarget's allocation
        return fRenderTargetProxy->instantiate(fContext->textureProvider());
    }

    GrTextureProxy* asDeferredTexture();

    sk_sp<GrTexture> asTexture() {
        if (!this->accessRenderTarget()) {
            return nullptr;
        }

        // TODO: usage of this entry point needs to be reduced and potentially eliminated
        // since it ends the deferral of the GrRenderTarget's allocation
        // It's usage should migrate to asDeferredTexture
        return sk_ref_sp(this->accessRenderTarget()->asTexture());
    }

    // Provides access to functions that aren't part of the public API.
    GrRenderTargetContextPriv priv();
    const GrRenderTargetContextPriv priv() const;

    GrAuditTrail* auditTrail() { return fAuditTrail; }

    bool isWrapped_ForTesting() const;

protected:
    GrRenderTargetContext(GrContext*, GrDrawingManager*, sk_sp<GrRenderTargetProxy>,
                          sk_sp<SkColorSpace>, const SkSurfaceProps* surfaceProps, GrAuditTrail*,
                          GrSingleOwner*);

    GrDrawingManager* drawingManager() { return fDrawingManager; }

    SkDEBUGCODE(GrSingleOwner* singleOwner() { return fSingleOwner; })
    SkDEBUGCODE(void validate() const;)

private:
    friend class GrAtlasTextBlob; // for access to drawBatch
    friend class GrStencilAndCoverTextContext; // for access to drawBatch

    friend class GrDrawingManager; // for ctor
    friend class GrRenderTargetContextPriv;
    friend class GrTestTarget;  // for access to getOpList
    friend class GrSWMaskHelper;                 // for access to drawBatch

    // All the path renderers currently make their own batches
    friend class GrSoftwarePathRenderer;         // for access to drawBatch
    friend class GrAAConvexPathRenderer;         // for access to drawBatch
    friend class GrDashLinePathRenderer;         // for access to drawBatch
    friend class GrAAHairLinePathRenderer;       // for access to drawBatch
    friend class GrAALinearizingConvexPathRenderer;  // for access to drawBatch
    friend class GrAADistanceFieldPathRenderer;  // for access to drawBatch
    friend class GrDefaultPathRenderer;          // for access to drawBatch
    friend class GrPLSPathRenderer;              // for access to drawBatch
    friend class GrMSAAPathRenderer;             // for access to drawBatch
    friend class GrStencilAndCoverPathRenderer;  // for access to drawBatch
    friend class GrTessellatingPathRenderer;     // for access to drawBatch

    void internalClear(const GrFixedClip&, const GrColor, bool canIgnoreClip);

    bool drawFilledDRRect(const GrClip& clip,
                          const GrPaint& paint,
                          const SkMatrix& viewMatrix,
                          const SkRRect& origOuter,
                          const SkRRect& origInner);

    bool drawFilledRect(const GrClip& clip,
                        const GrPaint& paint,
                        const SkMatrix& viewMatrix,
                        const SkRect& rect,
                        const GrUserStencilSettings* ss);

    void drawNonAAFilledRect(const GrClip&,
                             const GrPaint&,
                             const SkMatrix& viewMatrix,
                             const SkRect& rect,
                             const SkRect* localRect,
                             const SkMatrix* localMatrix,
                             const GrUserStencilSettings* ss,
                             bool useHWAA);

    void internalDrawPath(const GrClip& clip,
                          const GrPaint& paint,
                          const SkMatrix& viewMatrix,
                          const SkPath& path,
                          const GrStyle& style);

    // This entry point allows the GrTextContext-derived classes to add their batches to
    // the GrOpList.
    void drawBatch(const GrPipelineBuilder& pipelineBuilder, const GrClip&, GrDrawBatch* batch);

    GrRenderTargetOpList* getOpList();

    GrDrawingManager*                 fDrawingManager;
    sk_sp<GrRenderTargetProxy>        fRenderTargetProxy;

    // In MDB-mode the GrOpList can be closed by some other renderTargetContext that has picked
    // it up. For this reason, the GrOpList should only ever be accessed via 'getOpList'.
    GrRenderTargetOpList*             fOpList;
    GrContext*                        fContext;
    GrInstancedPipelineInfo           fInstancedPipelineInfo;

    sk_sp<SkColorSpace>               fColorSpace;
    sk_sp<GrColorSpaceXform>          fColorXformFromSRGB;
    SkSurfaceProps                    fSurfaceProps;
    GrAuditTrail*                     fAuditTrail;

    // In debug builds we guard against improper thread handling
    SkDEBUGCODE(mutable GrSingleOwner* fSingleOwner;)
};

#endif
