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

#ifndef GrCoordTransform_DEFINED
#define GrCoordTransform_DEFINED

#include "include/core/SkMatrix.h"
#include "src/gpu/GrSurfaceProxyPriv.h"
#include "src/gpu/GrTextureProxy.h"

class GrTexture;

/**
 * A class representing a linear transformation of local coordinates. GrFragnentProcessors
 * these transformations, and the GrGeometryProcessor implements the transformation.
 */
class GrCoordTransform {
public:
    GrCoordTransform() = default;

    GrCoordTransform(const GrCoordTransform&) = default;

    /**
     * Create a transformation that maps [0, proxy->width()] x [0, proxy->height()] to a proxy's
     * extent.
     */
    GrCoordTransform(GrSurfaceProxy* proxy) : fProxy(proxy) {}

    /**
     * Create a transformation from a matrix. The proxy origin also implies whether a y-reversal
     * should be performed.
     */
    GrCoordTransform(const SkMatrix& m, GrSurfaceProxy* proxy) : fProxy(proxy), fMatrix(m) {
        SkASSERT(proxy);
    }

    /**
     * Create a transformation that applies the matrix to a coord set.
     */
    GrCoordTransform(const SkMatrix& m) : fMatrix(m) {}

    GrCoordTransform& operator=(const GrCoordTransform& that) = default;

    // The textures' effect is to optionally normalize the final matrix, so a blind equality check
    // could be misleading.
    bool operator==(const GrCoordTransform& that) const = delete;
    bool operator!=(const GrCoordTransform& that) const = delete;

    bool hasSameEffectiveMatrix(const GrCoordTransform& that) const {
        // This is slightly more conservative than computing each transforms effective matrix and
        // then comparing them.
        if (!fMatrix.cheapEqualTo(that.fMatrix)) {
            return false;
        }
        if (SkToBool(fProxy) != SkToBool(that.fProxy)) {
            return false;
        }
        if (this->normalize() != that.normalize() || this->reverseY() != that.reverseY()) {
            return false;
        }
        if (this->normalize() &&
            fProxy->backingStoreDimensions() != that.fProxy->backingStoreDimensions()) {
            return false;
        }
        return true;
    }

    const SkMatrix& matrix() const { return fMatrix; }
    const GrSurfaceProxy* proxy() const { return fProxy; }
    bool normalize() const {
        return fProxy && fProxy->backendFormat().textureType() != GrTextureType::kRectangle;
    }
    bool reverseY() const { return fProxy && fProxy->origin() == kBottomLeft_GrSurfaceOrigin; }
    bool isNoOp() const { return fMatrix.isIdentity() && !this->normalize() && !this->reverseY(); }

    // This should only ever be called at flush time after the backing texture has been
    // successfully instantiated
    GrTexture* peekTexture() const { return fProxy->peekTexture(); }

private:
    const GrSurfaceProxy* fProxy = nullptr;
    SkMatrix fMatrix = SkMatrix::I();
};

#endif
