import { renderer } from "./../video.js";
import * as fileUtil from "./../../utils/file.js";
import { TextureAtlas, createAtlas } from "./atlas.js";
import { isPowerOfTwo } from "./../../math/math.js";
import { ArrayMultimap } from "@teppeis/multimaps";

/**
 * a basic texture cache object
 * @ignore
 */
class TextureCache {

    /**
     * @ignore
     */
    constructor(max_size = Infinity) {
        // cache uses an array to allow for duplicated key
        this.cache = new ArrayMultimap();
        this.tinted = new Map();
        this.units = new Map();
        this.usedUnits = new Set();
        this.max_size = max_size;
        this.clear();
    }

    /**
     * @ignore
     */
    clear() {
        this.cache.clear();
        this.tinted.clear();
        this.units.clear();
        this.usedUnits.clear();
    }

    /**
     * @ignore
     */
    allocateTextureUnit() {
        // find the first unit available among the max_size
        for (let unit = 0; unit < this.max_size; unit++) {
            // Check if unit is available
            if (!this.usedUnits.has(unit)) {
                // Add to used set
                this.usedUnits.add(unit);
                // return the new unit
                return unit;
            }
        }

        // No units available
        // TODO: Merge textures instead of throwing an exception
        throw new Error(
            "Texture cache overflow: " + this.max_size +
            " texture units available for this GPU."
        );
    }

    /**
     * @ignore
     */
    freeTextureUnit(texture) {
        let unit = this.units.get(texture);
        // was a texture unit allocated ?
        if (typeof unit !== "undefined") {
            this.usedUnits.delete(unit);
            this.units.delete(texture);
        }
    }

    /**
     * @ignore
     */
    get(image, atlas) {
        let entry;

        if (typeof atlas === "undefined") {
            entry = this.cache.get(image)[0];
        } else {
            // manage cases where a specific atlas is specified
            this.cache.forEach((value, key) => {
                let _atlas = value.getAtlas()[0];
                if (key === image && _atlas.width === atlas.framewidth && _atlas.height === atlas.frameheight) {
                    entry = value;
                }
            });
        }

        if (typeof entry === "undefined") {
            if (!atlas) {
                atlas = createAtlas(image.width, image.height, image.src ? fileUtil.getBasename(image.src) : undefined);
            }
            entry = new TextureAtlas(atlas, image, false);
            this.set(image, entry);
        }

        return entry;
    }

    /**
     * @ignore
     */
    delete(image) {
        if (this.cache.has(image)) {
            let texture = this.cache.get(image)[0];
            if (typeof texture !== "undefined") {
                this.freeTextureUnit(texture);
            }
            this.cache.delete(image);
        }
    }

    /**
     * @ignore
     */
    tint(src, color) {
        // make sure the src is in the cache
        let image_cache = this.tinted.get(src);

        if (image_cache === undefined) {
            image_cache = this.tinted.set(src, new Map());
        }

        if (!image_cache.has(color)) {
            image_cache.set(color, renderer.tint(src, color, "multiply"));
        }

        return image_cache.get(color);
    }

    /**
     * @ignore
     */
    set(image, texture) {
        let width = image.width;
        let height = image.height;

        // warn if a non POT texture is added to the cache when using WebGL1
        if (renderer.WebGLVersion === 1 && (!isPowerOfTwo(width) || !isPowerOfTwo(height))) {
            let src = typeof image.src !== "undefined" ? image.src : image;
            console.warn(
                "[Texture] " + src + " is not a POT texture " +
                "(" + width + "x" + height + ")"
            );
        }
        return this.cache.put(image, texture);
    }

    /**
     * @ignore
     */
    getUnit(texture) {
        if (!this.units.has(texture)) {
            this.units.set(texture, this.allocateTextureUnit());
        }
        return this.units.get(texture);
    }
}
export default TextureCache;
Powered by webdoc!