const registeredTiles = new Map() const pendingTiles = new Map() /** Implements a baseTexture cache. The last textures are kept for reuse */ let keepTextures = 0 const keptTextures = [] /** The current Tile implementation simply uses PIXI.Sprites. * * BTW: PIXI.extras.TilingSprite is not appropriate. It should be used for * repeating patterns. **/ export default class Tile extends PIXI.Sprite { constructor(texture, url) { super(texture) this.url = url this.register(url) } /** * Static method to enable keeping of base textures * * @static * @param {*} value * @memberof Tile */ static enableKeepTextures(value = 1000) { keepTextures = value } /** * Marks the given url as pending. Pending tiles should not be destroyed * * @static * @param {*} url * @memberof Tile */ static schedule(url) { let count = 0 if (pendingTiles.has(url)) { count = pendingTiles.get(url) } pendingTiles.set(url, count + 1) // console.log("Tile.scheduled", url, pendingTiles.size) } /** * Returns true iff the url is pending * * @static * @param {*} url * @returns * @memberof Tile */ static isPending(url) { return pendingTiles.has(url) && pendingTiles.get(url) > 0 } static isObsolete(url) { if (registeredTiles.has(url) && registeredTiles.get(url) > 0) { return false } return true } /** * Removes the given url from pending urls. * * @static * @param {*} url * @memberof Tile */ static unschedule(url) { if (pendingTiles.has(url)) { let count = pendingTiles.get(url) if (count > 1) { pendingTiles.set(url, count - 1) } else { pendingTiles.clear(url) } } // console.log("Tile.unscheduled", url, pendingTiles.size) } /** * Loads a tile from image using the PIXI.Texture.fromImage method. * * @static * @param {*} imageId * @param {*} crossorigin * @param {*} scaleMode * @returns * @memberof Tile */ static fromImage(imageId, crossorigin, scaleMode) { return new Tile(PIXI.Texture.fromImage(imageId, crossorigin, scaleMode), imageId) } /** * Registers the tile in the global reference counter for textures * * @param {*} url * @param {boolean} [debug=false] * @memberof Tile */ register(url, debug = false) { Tile.unschedule(url) if (registeredTiles.has(url)) { let tiles = registeredTiles.get(url) tiles.add(this) if (debug) console.log("Tile.register", url, tiles.size) } else { registeredTiles.set(url, new Set([this])) if (debug) console.log("Tile.register", url, 1) } } /** * Unregisters the rile in the global reference counter for textures * * @returns {number} The number of how often a texture is used. * @memberof Tile */ unregister() { let tiles = registeredTiles.get(this.url) tiles.delete(this) if (tiles.size == 0) { registeredTiles.delete(this.url) return 0 } return tiles.size } /** * Destroys this sprite and optionally its texture and children * * @param {*} options Part of the PIXI API, but ignored in the implementation * @memberof Tile */ destroy(options, debug = true) { let count = this.unregister() if (keepTextures > 0) { keptTextures.push({ url: this.url, texture: this.texture}) let opts = { children: true, texture: false, baseTexture: false } if (debug) console.log("Tile.destroy", registeredTiles.size, opts) super.destroy(opts) while(keptTextures.length > keepTextures) { let {url, texture} = keptTextures.shift() if (Tile.isObsolete(url)) { texture.destroy(true) // Destroy base as well if (debug) console.log("Destroying texture and baseTexture", url) } } } else { // No longer registered and not pending if (count <= 0 && !Tile.isPending(this.url)) { let opts = { children: true, texture: true, baseTexture: true } super.destroy(opts) if (debug) console.log("Tile.destroy", registeredTiles.size, opts) } else { let opts = { children: true, texture: false, baseTexture: false } if (debug) console.log("Tile.destroy", registeredTiles.size, opts) super.destroy(opts) } if (this.parent != null) { // UO: Emit warning and remove console.warn("Destroying tile with parent. Hiding instead") this.visible = false } } } /** * Returns an available texture that can be reused * * @param {*} url * @returns * @memberof Tile */ static textureAvailable(url) { if (registeredTiles.has(url)) { let tiles = registeredTiles.get(url) for (let tile of tiles.values()) { //console.log("Reusing cached texture", tile.parent) return tile.texture } } return null } /** * Texture received too late. We do not need it. * @param {*} url * @param {*} texture */ static lateTexture(url, texture) { let destroyBase = !registeredTiles.has(url) texture.destroy(destroyBase) } static printInfos() { let references = new Map() let multiples = 0 for (let [url, tiles] of registeredTiles.entries()) { let count = tiles.size references.set(url, count) if (count > 1) { multiples += 1 } } console.log({ multiples, references }) } }