/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ANDROID_HWUI_UV_MAPPER_H
#define ANDROID_HWUI_UV_MAPPER_H

#include "Rect.h"

namespace android {
namespace uirenderer {

/**
 * This class can be used to map UV coordinates from the [0..1]
 * range to other arbitrary ranges. All the methods below assume
 * that the input values lie in the [0..1] range already.
 */
class UvMapper {
public:
    /**
     * Using this constructor is equivalent to not using any mapping at all.
     * UV coordinates in the [0..1] range remain in the [0..1] range.
     */
    UvMapper() : mIdentity(true), mMinU(0.0f), mMaxU(1.0f), mMinV(0.0f), mMaxV(1.0f) {}

    /**
     * Creates a new mapper with the specified ranges for U and V coordinates.
     * The parameter minU must be < maxU and minV must be < maxV.
     */
    UvMapper(float minU, float maxU, float minV, float maxV)
            : mMinU(minU), mMaxU(maxU), mMinV(minV), mMaxV(maxV) {
        checkIdentity();
    }

    /**
     * Returns true if calling the map*() methods has no effect (that is,
     * texture coordinates remain in the 0..1 range.)
     */
    bool isIdentity() const { return mIdentity; }

    /**
     * Changes the U and V mapping ranges.
     * The parameter minU must be < maxU and minV must be < maxV.
     */
    void setMapping(float minU, float maxU, float minV, float maxV) {
        mMinU = minU;
        mMaxU = maxU;
        mMinV = minV;
        mMaxV = maxV;
        checkIdentity();
    }

    /**
     * Maps a single value in the U range.
     */
    void mapU(float& u) const {
        if (!mIdentity) u = lerp(mMinU, mMaxU, u);
    }

    /**
     * Maps a single value in the V range.
     */
    void mapV(float& v) const {
        if (!mIdentity) v = lerp(mMinV, mMaxV, v);
    }

    /**
     * Maps the specified rectangle in place. This method assumes:
     * - left = min. U
     * - top = min. V
     * - right = max. U
     * - bottom = max. V
     */
    void map(Rect& texCoords) const {
        if (!mIdentity) {
            texCoords.left = lerp(mMinU, mMaxU, texCoords.left);
            texCoords.right = lerp(mMinU, mMaxU, texCoords.right);
            texCoords.top = lerp(mMinV, mMaxV, texCoords.top);
            texCoords.bottom = lerp(mMinV, mMaxV, texCoords.bottom);
        }
    }

    /**
     * Maps the specified UV coordinates in place.
     */
    void map(float& u1, float& v1, float& u2, float& v2) const {
        if (!mIdentity) {
            u1 = lerp(mMinU, mMaxU, u1);
            u2 = lerp(mMinU, mMaxU, u2);
            v1 = lerp(mMinV, mMaxV, v1);
            v2 = lerp(mMinV, mMaxV, v2);
        }
    }

    void dump() const {
        ALOGD("mapper[minU=%.2f maxU=%.2f minV=%.2f maxV=%.2f]", mMinU, mMaxU, mMinV, mMaxV);
    }

private:
    static float lerp(float start, float stop, float amount) {
        return start + (stop - start) * amount;
    }

    void checkIdentity() {
        mIdentity = mMinU == 0.0f && mMaxU == 1.0f && mMinV == 0.0f && mMaxV == 1.0f;
    }

    bool mIdentity;
    float mMinU;
    float mMaxU;
    float mMinV;
    float mMaxV;
};

}  // namespace uirenderer
}  // namespace android

#endif  // ANDROID_HWUI_UV_MAPPER_H
