diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 76c1079..9def67f 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1071,7 +1071,7 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode); + const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 76c1079..9def67f 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1071,7 +1071,7 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode); + const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 1557cf8..065a4f5 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -42,13 +42,16 @@ canvas.width = 3; canvas.height = 3; - const texture = Texture.fromCanvas(canvas); + const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text'); texture.orig = new Rectangle(); texture.trim = new Rectangle(); super(texture); + // base texture is already automatically added to the cache, now adding the actual texture + Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]); + /** * The canvas element that everything is drawn to * diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 76c1079..9def67f 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1071,7 +1071,7 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode); + const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 1557cf8..065a4f5 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -42,13 +42,16 @@ canvas.width = 3; canvas.height = 3; - const texture = Texture.fromCanvas(canvas); + const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text'); texture.orig = new Rectangle(); texture.trim = new Rectangle(); super(texture); + // base texture is already automatically added to the cache, now adding the actual texture + Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]); + /** * The canvas element that everything is drawn to * diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 0a9259b..094ad8b 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -196,12 +196,6 @@ this._enabled = 0; this._virtalBoundId = -1; - // if no source passed don't try to load - if (source) - { - this.loadSource(source); - } - /** * If the object has been destroyed via destroy(). If true, it should not be used. * @@ -212,6 +206,21 @@ this._destroyed = false; /** + * The ids under which this BaseTexture has been added to the base texture cache. This is + * automatically set as long as BaseTexture.addToCache is used, but may not be set if a + * BaseTexture is added directly to the BaseTextureCache array. + * + * @member {string[]} + */ + this.textureCacheIds = []; + + // if no source passed don't try to load + if (source) + { + this.loadSource(source); + } + + /** * Fired when a not-immediately-available source finishes loading. * * @protected @@ -577,7 +586,7 @@ this.source = canvas; // Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`) - BaseTextureCache[canvas._pixiId] = this; + BaseTexture.addToCache(this, canvas._pixiId); this.isLoading = false; this._sourceLoaded(); @@ -618,13 +627,9 @@ this.dispose(); - for (const prop in BaseTextureCache) - { - if (BaseTextureCache[prop] === this) - { - delete BaseTextureCache[prop]; - } - } + BaseTexture.removeFromCache(this); + this.textureCacheIds = null; + this._destroyed = true; } @@ -692,7 +697,7 @@ image.src = imageUrl; // Setting this triggers load - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -704,13 +709,14 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.BaseTexture} The new base texture. */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { if (!canvas._pixiId) { - canvas._pixiId = `canvas_${uid()}`; + canvas._pixiId = `${origin}_${uid()}`; } let baseTexture = BaseTextureCache[canvas._pixiId]; @@ -718,7 +724,7 @@ if (!baseTexture) { baseTexture = new BaseTexture(canvas, scaleMode); - BaseTextureCache[canvas._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, canvas._pixiId); } return baseTexture; @@ -758,7 +764,7 @@ // if there is an @2x at the end of the url we are going to assume its a highres image baseTexture.resolution = getResolutionOfUrl(imageUrl); - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -771,4 +777,75 @@ // lets assume its a base texture! return source; } + + /** + * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object. + * + * @static + * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache. + * @param {string} id - The id that the BaseTexture will be stored against. + */ + static addToCache(baseTexture, id) + { + if (id) + { + if (baseTexture.textureCacheIds.indexOf(id) === -1) + { + baseTexture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (BaseTextureCache[id]) + { + console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + BaseTextureCache[id] = baseTexture; + } + } + + /** + * Remove a BaseTexture from the global BaseTextureCache. + * + * @static + * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself. + * @return {PIXI.BaseTexture|null} The BaseTexture that was removed. + */ + static removeFromCache(baseTexture) + { + if (typeof baseTexture === 'string') + { + const baseTextureFromCache = BaseTextureCache[baseTexture]; + + if (baseTextureFromCache) + { + const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture); + + if (index > -1) + { + baseTextureFromCache.textureCacheIds.splice(index, 1); + } + + delete BaseTextureCache[baseTexture]; + + return baseTextureFromCache; + } + } + else + { + for (let i = 0; i < baseTexture.textureCacheIds.length; ++i) + { + delete BaseTextureCache[baseTexture.textureCacheIds[i]]; + } + + baseTexture.textureCacheIds.length = 0; + + return baseTexture; + } + + return null; + } } diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 76c1079..9def67f 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1071,7 +1071,7 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode); + const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 1557cf8..065a4f5 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -42,13 +42,16 @@ canvas.width = 3; canvas.height = 3; - const texture = Texture.fromCanvas(canvas); + const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text'); texture.orig = new Rectangle(); texture.trim = new Rectangle(); super(texture); + // base texture is already automatically added to the cache, now adding the actual texture + Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]); + /** * The canvas element that everything is drawn to * diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 0a9259b..094ad8b 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -196,12 +196,6 @@ this._enabled = 0; this._virtalBoundId = -1; - // if no source passed don't try to load - if (source) - { - this.loadSource(source); - } - /** * If the object has been destroyed via destroy(). If true, it should not be used. * @@ -212,6 +206,21 @@ this._destroyed = false; /** + * The ids under which this BaseTexture has been added to the base texture cache. This is + * automatically set as long as BaseTexture.addToCache is used, but may not be set if a + * BaseTexture is added directly to the BaseTextureCache array. + * + * @member {string[]} + */ + this.textureCacheIds = []; + + // if no source passed don't try to load + if (source) + { + this.loadSource(source); + } + + /** * Fired when a not-immediately-available source finishes loading. * * @protected @@ -577,7 +586,7 @@ this.source = canvas; // Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`) - BaseTextureCache[canvas._pixiId] = this; + BaseTexture.addToCache(this, canvas._pixiId); this.isLoading = false; this._sourceLoaded(); @@ -618,13 +627,9 @@ this.dispose(); - for (const prop in BaseTextureCache) - { - if (BaseTextureCache[prop] === this) - { - delete BaseTextureCache[prop]; - } - } + BaseTexture.removeFromCache(this); + this.textureCacheIds = null; + this._destroyed = true; } @@ -692,7 +697,7 @@ image.src = imageUrl; // Setting this triggers load - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -704,13 +709,14 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.BaseTexture} The new base texture. */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { if (!canvas._pixiId) { - canvas._pixiId = `canvas_${uid()}`; + canvas._pixiId = `${origin}_${uid()}`; } let baseTexture = BaseTextureCache[canvas._pixiId]; @@ -718,7 +724,7 @@ if (!baseTexture) { baseTexture = new BaseTexture(canvas, scaleMode); - BaseTextureCache[canvas._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, canvas._pixiId); } return baseTexture; @@ -758,7 +764,7 @@ // if there is an @2x at the end of the url we are going to assume its a highres image baseTexture.resolution = getResolutionOfUrl(imageUrl); - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -771,4 +777,75 @@ // lets assume its a base texture! return source; } + + /** + * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object. + * + * @static + * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache. + * @param {string} id - The id that the BaseTexture will be stored against. + */ + static addToCache(baseTexture, id) + { + if (id) + { + if (baseTexture.textureCacheIds.indexOf(id) === -1) + { + baseTexture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (BaseTextureCache[id]) + { + console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + BaseTextureCache[id] = baseTexture; + } + } + + /** + * Remove a BaseTexture from the global BaseTextureCache. + * + * @static + * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself. + * @return {PIXI.BaseTexture|null} The BaseTexture that was removed. + */ + static removeFromCache(baseTexture) + { + if (typeof baseTexture === 'string') + { + const baseTextureFromCache = BaseTextureCache[baseTexture]; + + if (baseTextureFromCache) + { + const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture); + + if (index > -1) + { + baseTextureFromCache.textureCacheIds.splice(index, 1); + } + + delete BaseTextureCache[baseTexture]; + + return baseTextureFromCache; + } + } + else + { + for (let i = 0; i < baseTexture.textureCacheIds.length; ++i) + { + delete BaseTextureCache[baseTexture.textureCacheIds[i]]; + } + + baseTexture.textureCacheIds.length = 0; + + return baseTexture; + } + + return null; + } } diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 9b24f27..85abccf 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -1,5 +1,5 @@ import { Rectangle, Texture } from '../'; -import { getResolutionOfUrl, TextureCache } from '../utils'; +import { getResolutionOfUrl } from '../utils'; /** * Utility class for maintaining reference to a collection @@ -207,8 +207,7 @@ ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions - TextureCache[i] = this.textures[i]; - this.textures[i].textureCacheId = i; + Texture.addToCache(this.textures[i], i); } frameIndex++; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 76c1079..9def67f 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1071,7 +1071,7 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode); + const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 1557cf8..065a4f5 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -42,13 +42,16 @@ canvas.width = 3; canvas.height = 3; - const texture = Texture.fromCanvas(canvas); + const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text'); texture.orig = new Rectangle(); texture.trim = new Rectangle(); super(texture); + // base texture is already automatically added to the cache, now adding the actual texture + Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]); + /** * The canvas element that everything is drawn to * diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 0a9259b..094ad8b 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -196,12 +196,6 @@ this._enabled = 0; this._virtalBoundId = -1; - // if no source passed don't try to load - if (source) - { - this.loadSource(source); - } - /** * If the object has been destroyed via destroy(). If true, it should not be used. * @@ -212,6 +206,21 @@ this._destroyed = false; /** + * The ids under which this BaseTexture has been added to the base texture cache. This is + * automatically set as long as BaseTexture.addToCache is used, but may not be set if a + * BaseTexture is added directly to the BaseTextureCache array. + * + * @member {string[]} + */ + this.textureCacheIds = []; + + // if no source passed don't try to load + if (source) + { + this.loadSource(source); + } + + /** * Fired when a not-immediately-available source finishes loading. * * @protected @@ -577,7 +586,7 @@ this.source = canvas; // Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`) - BaseTextureCache[canvas._pixiId] = this; + BaseTexture.addToCache(this, canvas._pixiId); this.isLoading = false; this._sourceLoaded(); @@ -618,13 +627,9 @@ this.dispose(); - for (const prop in BaseTextureCache) - { - if (BaseTextureCache[prop] === this) - { - delete BaseTextureCache[prop]; - } - } + BaseTexture.removeFromCache(this); + this.textureCacheIds = null; + this._destroyed = true; } @@ -692,7 +697,7 @@ image.src = imageUrl; // Setting this triggers load - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -704,13 +709,14 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.BaseTexture} The new base texture. */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { if (!canvas._pixiId) { - canvas._pixiId = `canvas_${uid()}`; + canvas._pixiId = `${origin}_${uid()}`; } let baseTexture = BaseTextureCache[canvas._pixiId]; @@ -718,7 +724,7 @@ if (!baseTexture) { baseTexture = new BaseTexture(canvas, scaleMode); - BaseTextureCache[canvas._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, canvas._pixiId); } return baseTexture; @@ -758,7 +764,7 @@ // if there is an @2x at the end of the url we are going to assume its a highres image baseTexture.resolution = getResolutionOfUrl(imageUrl); - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -771,4 +777,75 @@ // lets assume its a base texture! return source; } + + /** + * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object. + * + * @static + * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache. + * @param {string} id - The id that the BaseTexture will be stored against. + */ + static addToCache(baseTexture, id) + { + if (id) + { + if (baseTexture.textureCacheIds.indexOf(id) === -1) + { + baseTexture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (BaseTextureCache[id]) + { + console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + BaseTextureCache[id] = baseTexture; + } + } + + /** + * Remove a BaseTexture from the global BaseTextureCache. + * + * @static + * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself. + * @return {PIXI.BaseTexture|null} The BaseTexture that was removed. + */ + static removeFromCache(baseTexture) + { + if (typeof baseTexture === 'string') + { + const baseTextureFromCache = BaseTextureCache[baseTexture]; + + if (baseTextureFromCache) + { + const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture); + + if (index > -1) + { + baseTextureFromCache.textureCacheIds.splice(index, 1); + } + + delete BaseTextureCache[baseTexture]; + + return baseTextureFromCache; + } + } + else + { + for (let i = 0; i < baseTexture.textureCacheIds.length; ++i) + { + delete BaseTextureCache[baseTexture.textureCacheIds[i]]; + } + + baseTexture.textureCacheIds.length = 0; + + return baseTexture; + } + + return null; + } } diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 9b24f27..85abccf 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -1,5 +1,5 @@ import { Rectangle, Texture } from '../'; -import { getResolutionOfUrl, TextureCache } from '../utils'; +import { getResolutionOfUrl } from '../utils'; /** * Utility class for maintaining reference to a collection @@ -207,8 +207,7 @@ ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions - TextureCache[i] = this.textures[i]; - this.textures[i].textureCacheId = i; + Texture.addToCache(this.textures[i], i); } frameIndex++; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index bf2ae7b..6c4d324 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -3,7 +3,8 @@ import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; import { Rectangle } from '../math'; -import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils'; +import { TextureCache, getResolutionOfUrl } from '../utils'; +import settings from '../settings'; /** * A texture stores the information that represents an image or part of an image. It cannot be added @@ -158,13 +159,13 @@ this.transform = null; /** - * The id under which this Texture has been added to the texture cache. This is - * automatically set in certain cases, but may not always be accurate, particularly if - * the texture is in the cache under multiple ids. + * The ids under which this Texture has been added to the texture cache. This is + * automatically set as long as Texture.addToCache is used, but may not be set if a + * Texture is added directly to the TextureCache array. * - * @member {string} + * @member {string[]} */ - this.textureCacheId = null; + this.textureCacheIds = []; } /** @@ -231,7 +232,7 @@ // this only needs to be removed if the base texture is actually destroyed too.. if (TextureCache[this.baseTexture.imageUrl]) { - delete TextureCache[this.baseTexture.imageUrl]; + Texture.removeFromCache(this.baseTexture.imageUrl); } this.baseTexture.destroy(); @@ -247,17 +248,11 @@ this._uvs = null; this.trim = null; this.orig = null; - this.textureCacheId = null; this.valid = false; - for (const prop in TextureCache) - { - if (TextureCache[prop] === this) - { - delete TextureCache[prop]; - } - } + Texture.removeFromCache(this); + this.textureCacheIds = null; } /** @@ -305,7 +300,7 @@ if (!texture) { texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale)); - TextureCache[imageUrl] = texture; + Texture.addToCache(texture, imageUrl); } return texture; @@ -337,11 +332,12 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.Texture} The newly created texture */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { - return new Texture(BaseTexture.fromCanvas(canvas, scaleMode)); + return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin)); } /** @@ -413,7 +409,7 @@ } else if (source instanceof HTMLCanvasElement) { - return Texture.fromCanvas(source); + return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement'); } else if (source instanceof HTMLVideoElement) { @@ -452,51 +448,88 @@ } // lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions - BaseTextureCache[name] = baseTexture; - TextureCache[name] = texture; - texture.textureCacheId = name; + BaseTexture.addToCache(texture.baseTexture, name); + Texture.addToCache(texture, name); // also add references by url if they are different. if (name !== imageUrl) { - BaseTextureCache[imageUrl] = baseTexture; - TextureCache[imageUrl] = texture; + BaseTexture.addToCache(texture.baseTexture, imageUrl); + Texture.addToCache(texture, imageUrl); } return texture; } /** - * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object. + * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object. * * @static * @param {PIXI.Texture} texture - The Texture to add to the cache. - * @param {string} id - The id that the texture will be stored against. + * @param {string} id - The id that the Texture will be stored against. */ - static addTextureToCache(texture, id) + static addToCache(texture, id) { - if (!texture.textureCacheId) + if (id) { - texture.textureCacheId = id; + if (texture.textureCacheIds.indexOf(id) === -1) + { + texture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (TextureCache[id]) + { + console.warn(`Texture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + TextureCache[id] = texture; } - TextureCache[id] = texture; } /** - * Remove a texture from the global TextureCache. + * Remove a Texture from the global TextureCache. * * @static - * @param {string} id - The id of the texture to be removed - * @return {PIXI.Texture} The texture that was removed + * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself + * @return {PIXI.Texture|null} The Texture that was removed */ - static removeTextureFromCache(id) + static removeFromCache(texture) { - const texture = TextureCache[id]; + if (typeof texture === 'string') + { + const textureFromCache = TextureCache[texture]; - delete TextureCache[id]; - delete BaseTextureCache[id]; + if (textureFromCache) + { + const index = textureFromCache.textureCacheIds.indexOf(texture); - return texture; + if (index > -1) + { + textureFromCache.textureCacheIds.splice(index, 1); + } + + delete TextureCache[texture]; + + return textureFromCache; + } + } + else + { + for (let i = 0; i < texture.textureCacheIds.length; ++i) + { + delete TextureCache[texture.textureCacheIds[i]]; + } + + texture.textureCacheIds.length = 0; + + return texture; + } + + return null; } /** diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 76c1079..9def67f 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1071,7 +1071,7 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode); + const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 1557cf8..065a4f5 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -42,13 +42,16 @@ canvas.width = 3; canvas.height = 3; - const texture = Texture.fromCanvas(canvas); + const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text'); texture.orig = new Rectangle(); texture.trim = new Rectangle(); super(texture); + // base texture is already automatically added to the cache, now adding the actual texture + Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]); + /** * The canvas element that everything is drawn to * diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 0a9259b..094ad8b 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -196,12 +196,6 @@ this._enabled = 0; this._virtalBoundId = -1; - // if no source passed don't try to load - if (source) - { - this.loadSource(source); - } - /** * If the object has been destroyed via destroy(). If true, it should not be used. * @@ -212,6 +206,21 @@ this._destroyed = false; /** + * The ids under which this BaseTexture has been added to the base texture cache. This is + * automatically set as long as BaseTexture.addToCache is used, but may not be set if a + * BaseTexture is added directly to the BaseTextureCache array. + * + * @member {string[]} + */ + this.textureCacheIds = []; + + // if no source passed don't try to load + if (source) + { + this.loadSource(source); + } + + /** * Fired when a not-immediately-available source finishes loading. * * @protected @@ -577,7 +586,7 @@ this.source = canvas; // Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`) - BaseTextureCache[canvas._pixiId] = this; + BaseTexture.addToCache(this, canvas._pixiId); this.isLoading = false; this._sourceLoaded(); @@ -618,13 +627,9 @@ this.dispose(); - for (const prop in BaseTextureCache) - { - if (BaseTextureCache[prop] === this) - { - delete BaseTextureCache[prop]; - } - } + BaseTexture.removeFromCache(this); + this.textureCacheIds = null; + this._destroyed = true; } @@ -692,7 +697,7 @@ image.src = imageUrl; // Setting this triggers load - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -704,13 +709,14 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.BaseTexture} The new base texture. */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { if (!canvas._pixiId) { - canvas._pixiId = `canvas_${uid()}`; + canvas._pixiId = `${origin}_${uid()}`; } let baseTexture = BaseTextureCache[canvas._pixiId]; @@ -718,7 +724,7 @@ if (!baseTexture) { baseTexture = new BaseTexture(canvas, scaleMode); - BaseTextureCache[canvas._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, canvas._pixiId); } return baseTexture; @@ -758,7 +764,7 @@ // if there is an @2x at the end of the url we are going to assume its a highres image baseTexture.resolution = getResolutionOfUrl(imageUrl); - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -771,4 +777,75 @@ // lets assume its a base texture! return source; } + + /** + * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object. + * + * @static + * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache. + * @param {string} id - The id that the BaseTexture will be stored against. + */ + static addToCache(baseTexture, id) + { + if (id) + { + if (baseTexture.textureCacheIds.indexOf(id) === -1) + { + baseTexture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (BaseTextureCache[id]) + { + console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + BaseTextureCache[id] = baseTexture; + } + } + + /** + * Remove a BaseTexture from the global BaseTextureCache. + * + * @static + * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself. + * @return {PIXI.BaseTexture|null} The BaseTexture that was removed. + */ + static removeFromCache(baseTexture) + { + if (typeof baseTexture === 'string') + { + const baseTextureFromCache = BaseTextureCache[baseTexture]; + + if (baseTextureFromCache) + { + const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture); + + if (index > -1) + { + baseTextureFromCache.textureCacheIds.splice(index, 1); + } + + delete BaseTextureCache[baseTexture]; + + return baseTextureFromCache; + } + } + else + { + for (let i = 0; i < baseTexture.textureCacheIds.length; ++i) + { + delete BaseTextureCache[baseTexture.textureCacheIds[i]]; + } + + baseTexture.textureCacheIds.length = 0; + + return baseTexture; + } + + return null; + } } diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 9b24f27..85abccf 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -1,5 +1,5 @@ import { Rectangle, Texture } from '../'; -import { getResolutionOfUrl, TextureCache } from '../utils'; +import { getResolutionOfUrl } from '../utils'; /** * Utility class for maintaining reference to a collection @@ -207,8 +207,7 @@ ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions - TextureCache[i] = this.textures[i]; - this.textures[i].textureCacheId = i; + Texture.addToCache(this.textures[i], i); } frameIndex++; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index bf2ae7b..6c4d324 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -3,7 +3,8 @@ import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; import { Rectangle } from '../math'; -import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils'; +import { TextureCache, getResolutionOfUrl } from '../utils'; +import settings from '../settings'; /** * A texture stores the information that represents an image or part of an image. It cannot be added @@ -158,13 +159,13 @@ this.transform = null; /** - * The id under which this Texture has been added to the texture cache. This is - * automatically set in certain cases, but may not always be accurate, particularly if - * the texture is in the cache under multiple ids. + * The ids under which this Texture has been added to the texture cache. This is + * automatically set as long as Texture.addToCache is used, but may not be set if a + * Texture is added directly to the TextureCache array. * - * @member {string} + * @member {string[]} */ - this.textureCacheId = null; + this.textureCacheIds = []; } /** @@ -231,7 +232,7 @@ // this only needs to be removed if the base texture is actually destroyed too.. if (TextureCache[this.baseTexture.imageUrl]) { - delete TextureCache[this.baseTexture.imageUrl]; + Texture.removeFromCache(this.baseTexture.imageUrl); } this.baseTexture.destroy(); @@ -247,17 +248,11 @@ this._uvs = null; this.trim = null; this.orig = null; - this.textureCacheId = null; this.valid = false; - for (const prop in TextureCache) - { - if (TextureCache[prop] === this) - { - delete TextureCache[prop]; - } - } + Texture.removeFromCache(this); + this.textureCacheIds = null; } /** @@ -305,7 +300,7 @@ if (!texture) { texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale)); - TextureCache[imageUrl] = texture; + Texture.addToCache(texture, imageUrl); } return texture; @@ -337,11 +332,12 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.Texture} The newly created texture */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { - return new Texture(BaseTexture.fromCanvas(canvas, scaleMode)); + return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin)); } /** @@ -413,7 +409,7 @@ } else if (source instanceof HTMLCanvasElement) { - return Texture.fromCanvas(source); + return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement'); } else if (source instanceof HTMLVideoElement) { @@ -452,51 +448,88 @@ } // lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions - BaseTextureCache[name] = baseTexture; - TextureCache[name] = texture; - texture.textureCacheId = name; + BaseTexture.addToCache(texture.baseTexture, name); + Texture.addToCache(texture, name); // also add references by url if they are different. if (name !== imageUrl) { - BaseTextureCache[imageUrl] = baseTexture; - TextureCache[imageUrl] = texture; + BaseTexture.addToCache(texture.baseTexture, imageUrl); + Texture.addToCache(texture, imageUrl); } return texture; } /** - * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object. + * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object. * * @static * @param {PIXI.Texture} texture - The Texture to add to the cache. - * @param {string} id - The id that the texture will be stored against. + * @param {string} id - The id that the Texture will be stored against. */ - static addTextureToCache(texture, id) + static addToCache(texture, id) { - if (!texture.textureCacheId) + if (id) { - texture.textureCacheId = id; + if (texture.textureCacheIds.indexOf(id) === -1) + { + texture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (TextureCache[id]) + { + console.warn(`Texture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + TextureCache[id] = texture; } - TextureCache[id] = texture; } /** - * Remove a texture from the global TextureCache. + * Remove a Texture from the global TextureCache. * * @static - * @param {string} id - The id of the texture to be removed - * @return {PIXI.Texture} The texture that was removed + * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself + * @return {PIXI.Texture|null} The Texture that was removed */ - static removeTextureFromCache(id) + static removeFromCache(texture) { - const texture = TextureCache[id]; + if (typeof texture === 'string') + { + const textureFromCache = TextureCache[texture]; - delete TextureCache[id]; - delete BaseTextureCache[id]; + if (textureFromCache) + { + const index = textureFromCache.textureCacheIds.indexOf(texture); - return texture; + if (index > -1) + { + textureFromCache.textureCacheIds.splice(index, 1); + } + + delete TextureCache[texture]; + + return textureFromCache; + } + } + else + { + for (let i = 0; i < texture.textureCacheIds.length; ++i) + { + delete TextureCache[texture.textureCacheIds[i]]; + } + + texture.textureCacheIds.length = 0; + + return texture; + } + + return null; } /** diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js index a9b9f51..65b74b9 100644 --- a/src/core/textures/VideoBaseTexture.js +++ b/src/core/textures/VideoBaseTexture.js @@ -193,7 +193,7 @@ if (this.source && this.source._pixiId) { - delete BaseTextureCache[this.source._pixiId]; + BaseTexture.removeFromCache(this.source._pixiId); delete this.source._pixiId; } @@ -220,7 +220,7 @@ if (!baseTexture) { baseTexture = new VideoBaseTexture(video, scaleMode); - BaseTextureCache[video._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, video._pixiId); } return baseTexture; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 76c1079..9def67f 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1071,7 +1071,7 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode); + const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 1557cf8..065a4f5 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -42,13 +42,16 @@ canvas.width = 3; canvas.height = 3; - const texture = Texture.fromCanvas(canvas); + const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text'); texture.orig = new Rectangle(); texture.trim = new Rectangle(); super(texture); + // base texture is already automatically added to the cache, now adding the actual texture + Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]); + /** * The canvas element that everything is drawn to * diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 0a9259b..094ad8b 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -196,12 +196,6 @@ this._enabled = 0; this._virtalBoundId = -1; - // if no source passed don't try to load - if (source) - { - this.loadSource(source); - } - /** * If the object has been destroyed via destroy(). If true, it should not be used. * @@ -212,6 +206,21 @@ this._destroyed = false; /** + * The ids under which this BaseTexture has been added to the base texture cache. This is + * automatically set as long as BaseTexture.addToCache is used, but may not be set if a + * BaseTexture is added directly to the BaseTextureCache array. + * + * @member {string[]} + */ + this.textureCacheIds = []; + + // if no source passed don't try to load + if (source) + { + this.loadSource(source); + } + + /** * Fired when a not-immediately-available source finishes loading. * * @protected @@ -577,7 +586,7 @@ this.source = canvas; // Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`) - BaseTextureCache[canvas._pixiId] = this; + BaseTexture.addToCache(this, canvas._pixiId); this.isLoading = false; this._sourceLoaded(); @@ -618,13 +627,9 @@ this.dispose(); - for (const prop in BaseTextureCache) - { - if (BaseTextureCache[prop] === this) - { - delete BaseTextureCache[prop]; - } - } + BaseTexture.removeFromCache(this); + this.textureCacheIds = null; + this._destroyed = true; } @@ -692,7 +697,7 @@ image.src = imageUrl; // Setting this triggers load - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -704,13 +709,14 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.BaseTexture} The new base texture. */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { if (!canvas._pixiId) { - canvas._pixiId = `canvas_${uid()}`; + canvas._pixiId = `${origin}_${uid()}`; } let baseTexture = BaseTextureCache[canvas._pixiId]; @@ -718,7 +724,7 @@ if (!baseTexture) { baseTexture = new BaseTexture(canvas, scaleMode); - BaseTextureCache[canvas._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, canvas._pixiId); } return baseTexture; @@ -758,7 +764,7 @@ // if there is an @2x at the end of the url we are going to assume its a highres image baseTexture.resolution = getResolutionOfUrl(imageUrl); - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -771,4 +777,75 @@ // lets assume its a base texture! return source; } + + /** + * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object. + * + * @static + * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache. + * @param {string} id - The id that the BaseTexture will be stored against. + */ + static addToCache(baseTexture, id) + { + if (id) + { + if (baseTexture.textureCacheIds.indexOf(id) === -1) + { + baseTexture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (BaseTextureCache[id]) + { + console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + BaseTextureCache[id] = baseTexture; + } + } + + /** + * Remove a BaseTexture from the global BaseTextureCache. + * + * @static + * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself. + * @return {PIXI.BaseTexture|null} The BaseTexture that was removed. + */ + static removeFromCache(baseTexture) + { + if (typeof baseTexture === 'string') + { + const baseTextureFromCache = BaseTextureCache[baseTexture]; + + if (baseTextureFromCache) + { + const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture); + + if (index > -1) + { + baseTextureFromCache.textureCacheIds.splice(index, 1); + } + + delete BaseTextureCache[baseTexture]; + + return baseTextureFromCache; + } + } + else + { + for (let i = 0; i < baseTexture.textureCacheIds.length; ++i) + { + delete BaseTextureCache[baseTexture.textureCacheIds[i]]; + } + + baseTexture.textureCacheIds.length = 0; + + return baseTexture; + } + + return null; + } } diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 9b24f27..85abccf 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -1,5 +1,5 @@ import { Rectangle, Texture } from '../'; -import { getResolutionOfUrl, TextureCache } from '../utils'; +import { getResolutionOfUrl } from '../utils'; /** * Utility class for maintaining reference to a collection @@ -207,8 +207,7 @@ ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions - TextureCache[i] = this.textures[i]; - this.textures[i].textureCacheId = i; + Texture.addToCache(this.textures[i], i); } frameIndex++; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index bf2ae7b..6c4d324 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -3,7 +3,8 @@ import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; import { Rectangle } from '../math'; -import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils'; +import { TextureCache, getResolutionOfUrl } from '../utils'; +import settings from '../settings'; /** * A texture stores the information that represents an image or part of an image. It cannot be added @@ -158,13 +159,13 @@ this.transform = null; /** - * The id under which this Texture has been added to the texture cache. This is - * automatically set in certain cases, but may not always be accurate, particularly if - * the texture is in the cache under multiple ids. + * The ids under which this Texture has been added to the texture cache. This is + * automatically set as long as Texture.addToCache is used, but may not be set if a + * Texture is added directly to the TextureCache array. * - * @member {string} + * @member {string[]} */ - this.textureCacheId = null; + this.textureCacheIds = []; } /** @@ -231,7 +232,7 @@ // this only needs to be removed if the base texture is actually destroyed too.. if (TextureCache[this.baseTexture.imageUrl]) { - delete TextureCache[this.baseTexture.imageUrl]; + Texture.removeFromCache(this.baseTexture.imageUrl); } this.baseTexture.destroy(); @@ -247,17 +248,11 @@ this._uvs = null; this.trim = null; this.orig = null; - this.textureCacheId = null; this.valid = false; - for (const prop in TextureCache) - { - if (TextureCache[prop] === this) - { - delete TextureCache[prop]; - } - } + Texture.removeFromCache(this); + this.textureCacheIds = null; } /** @@ -305,7 +300,7 @@ if (!texture) { texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale)); - TextureCache[imageUrl] = texture; + Texture.addToCache(texture, imageUrl); } return texture; @@ -337,11 +332,12 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.Texture} The newly created texture */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { - return new Texture(BaseTexture.fromCanvas(canvas, scaleMode)); + return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin)); } /** @@ -413,7 +409,7 @@ } else if (source instanceof HTMLCanvasElement) { - return Texture.fromCanvas(source); + return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement'); } else if (source instanceof HTMLVideoElement) { @@ -452,51 +448,88 @@ } // lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions - BaseTextureCache[name] = baseTexture; - TextureCache[name] = texture; - texture.textureCacheId = name; + BaseTexture.addToCache(texture.baseTexture, name); + Texture.addToCache(texture, name); // also add references by url if they are different. if (name !== imageUrl) { - BaseTextureCache[imageUrl] = baseTexture; - TextureCache[imageUrl] = texture; + BaseTexture.addToCache(texture.baseTexture, imageUrl); + Texture.addToCache(texture, imageUrl); } return texture; } /** - * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object. + * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object. * * @static * @param {PIXI.Texture} texture - The Texture to add to the cache. - * @param {string} id - The id that the texture will be stored against. + * @param {string} id - The id that the Texture will be stored against. */ - static addTextureToCache(texture, id) + static addToCache(texture, id) { - if (!texture.textureCacheId) + if (id) { - texture.textureCacheId = id; + if (texture.textureCacheIds.indexOf(id) === -1) + { + texture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (TextureCache[id]) + { + console.warn(`Texture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + TextureCache[id] = texture; } - TextureCache[id] = texture; } /** - * Remove a texture from the global TextureCache. + * Remove a Texture from the global TextureCache. * * @static - * @param {string} id - The id of the texture to be removed - * @return {PIXI.Texture} The texture that was removed + * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself + * @return {PIXI.Texture|null} The Texture that was removed */ - static removeTextureFromCache(id) + static removeFromCache(texture) { - const texture = TextureCache[id]; + if (typeof texture === 'string') + { + const textureFromCache = TextureCache[texture]; - delete TextureCache[id]; - delete BaseTextureCache[id]; + if (textureFromCache) + { + const index = textureFromCache.textureCacheIds.indexOf(texture); - return texture; + if (index > -1) + { + textureFromCache.textureCacheIds.splice(index, 1); + } + + delete TextureCache[texture]; + + return textureFromCache; + } + } + else + { + for (let i = 0; i < texture.textureCacheIds.length; ++i) + { + delete TextureCache[texture.textureCacheIds[i]]; + } + + texture.textureCacheIds.length = 0; + + return texture; + } + + return null; } /** diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js index a9b9f51..65b74b9 100644 --- a/src/core/textures/VideoBaseTexture.js +++ b/src/core/textures/VideoBaseTexture.js @@ -193,7 +193,7 @@ if (this.source && this.source._pixiId) { - delete BaseTextureCache[this.source._pixiId]; + BaseTexture.removeFromCache(this.source._pixiId); delete this.source._pixiId; } @@ -220,7 +220,7 @@ if (!baseTexture) { baseTexture = new VideoBaseTexture(video, scaleMode); - BaseTextureCache[video._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, video._pixiId); } return baseTexture; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 582b24a..098b383 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -368,7 +368,7 @@ * @memberof PIXI.utils * @private */ -export const TextureCache = {}; +export const TextureCache = Object.create(null); /** * @todo Describe property usage @@ -376,7 +376,7 @@ * @memberof PIXI.utils * @private */ -export const BaseTextureCache = {}; +export const BaseTextureCache = Object.create(null); /** * Destroys all texture in the cache diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 76c1079..9def67f 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1071,7 +1071,7 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode); + const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 1557cf8..065a4f5 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -42,13 +42,16 @@ canvas.width = 3; canvas.height = 3; - const texture = Texture.fromCanvas(canvas); + const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text'); texture.orig = new Rectangle(); texture.trim = new Rectangle(); super(texture); + // base texture is already automatically added to the cache, now adding the actual texture + Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]); + /** * The canvas element that everything is drawn to * diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 0a9259b..094ad8b 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -196,12 +196,6 @@ this._enabled = 0; this._virtalBoundId = -1; - // if no source passed don't try to load - if (source) - { - this.loadSource(source); - } - /** * If the object has been destroyed via destroy(). If true, it should not be used. * @@ -212,6 +206,21 @@ this._destroyed = false; /** + * The ids under which this BaseTexture has been added to the base texture cache. This is + * automatically set as long as BaseTexture.addToCache is used, but may not be set if a + * BaseTexture is added directly to the BaseTextureCache array. + * + * @member {string[]} + */ + this.textureCacheIds = []; + + // if no source passed don't try to load + if (source) + { + this.loadSource(source); + } + + /** * Fired when a not-immediately-available source finishes loading. * * @protected @@ -577,7 +586,7 @@ this.source = canvas; // Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`) - BaseTextureCache[canvas._pixiId] = this; + BaseTexture.addToCache(this, canvas._pixiId); this.isLoading = false; this._sourceLoaded(); @@ -618,13 +627,9 @@ this.dispose(); - for (const prop in BaseTextureCache) - { - if (BaseTextureCache[prop] === this) - { - delete BaseTextureCache[prop]; - } - } + BaseTexture.removeFromCache(this); + this.textureCacheIds = null; + this._destroyed = true; } @@ -692,7 +697,7 @@ image.src = imageUrl; // Setting this triggers load - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -704,13 +709,14 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.BaseTexture} The new base texture. */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { if (!canvas._pixiId) { - canvas._pixiId = `canvas_${uid()}`; + canvas._pixiId = `${origin}_${uid()}`; } let baseTexture = BaseTextureCache[canvas._pixiId]; @@ -718,7 +724,7 @@ if (!baseTexture) { baseTexture = new BaseTexture(canvas, scaleMode); - BaseTextureCache[canvas._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, canvas._pixiId); } return baseTexture; @@ -758,7 +764,7 @@ // if there is an @2x at the end of the url we are going to assume its a highres image baseTexture.resolution = getResolutionOfUrl(imageUrl); - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -771,4 +777,75 @@ // lets assume its a base texture! return source; } + + /** + * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object. + * + * @static + * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache. + * @param {string} id - The id that the BaseTexture will be stored against. + */ + static addToCache(baseTexture, id) + { + if (id) + { + if (baseTexture.textureCacheIds.indexOf(id) === -1) + { + baseTexture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (BaseTextureCache[id]) + { + console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + BaseTextureCache[id] = baseTexture; + } + } + + /** + * Remove a BaseTexture from the global BaseTextureCache. + * + * @static + * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself. + * @return {PIXI.BaseTexture|null} The BaseTexture that was removed. + */ + static removeFromCache(baseTexture) + { + if (typeof baseTexture === 'string') + { + const baseTextureFromCache = BaseTextureCache[baseTexture]; + + if (baseTextureFromCache) + { + const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture); + + if (index > -1) + { + baseTextureFromCache.textureCacheIds.splice(index, 1); + } + + delete BaseTextureCache[baseTexture]; + + return baseTextureFromCache; + } + } + else + { + for (let i = 0; i < baseTexture.textureCacheIds.length; ++i) + { + delete BaseTextureCache[baseTexture.textureCacheIds[i]]; + } + + baseTexture.textureCacheIds.length = 0; + + return baseTexture; + } + + return null; + } } diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 9b24f27..85abccf 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -1,5 +1,5 @@ import { Rectangle, Texture } from '../'; -import { getResolutionOfUrl, TextureCache } from '../utils'; +import { getResolutionOfUrl } from '../utils'; /** * Utility class for maintaining reference to a collection @@ -207,8 +207,7 @@ ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions - TextureCache[i] = this.textures[i]; - this.textures[i].textureCacheId = i; + Texture.addToCache(this.textures[i], i); } frameIndex++; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index bf2ae7b..6c4d324 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -3,7 +3,8 @@ import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; import { Rectangle } from '../math'; -import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils'; +import { TextureCache, getResolutionOfUrl } from '../utils'; +import settings from '../settings'; /** * A texture stores the information that represents an image or part of an image. It cannot be added @@ -158,13 +159,13 @@ this.transform = null; /** - * The id under which this Texture has been added to the texture cache. This is - * automatically set in certain cases, but may not always be accurate, particularly if - * the texture is in the cache under multiple ids. + * The ids under which this Texture has been added to the texture cache. This is + * automatically set as long as Texture.addToCache is used, but may not be set if a + * Texture is added directly to the TextureCache array. * - * @member {string} + * @member {string[]} */ - this.textureCacheId = null; + this.textureCacheIds = []; } /** @@ -231,7 +232,7 @@ // this only needs to be removed if the base texture is actually destroyed too.. if (TextureCache[this.baseTexture.imageUrl]) { - delete TextureCache[this.baseTexture.imageUrl]; + Texture.removeFromCache(this.baseTexture.imageUrl); } this.baseTexture.destroy(); @@ -247,17 +248,11 @@ this._uvs = null; this.trim = null; this.orig = null; - this.textureCacheId = null; this.valid = false; - for (const prop in TextureCache) - { - if (TextureCache[prop] === this) - { - delete TextureCache[prop]; - } - } + Texture.removeFromCache(this); + this.textureCacheIds = null; } /** @@ -305,7 +300,7 @@ if (!texture) { texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale)); - TextureCache[imageUrl] = texture; + Texture.addToCache(texture, imageUrl); } return texture; @@ -337,11 +332,12 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.Texture} The newly created texture */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { - return new Texture(BaseTexture.fromCanvas(canvas, scaleMode)); + return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin)); } /** @@ -413,7 +409,7 @@ } else if (source instanceof HTMLCanvasElement) { - return Texture.fromCanvas(source); + return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement'); } else if (source instanceof HTMLVideoElement) { @@ -452,51 +448,88 @@ } // lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions - BaseTextureCache[name] = baseTexture; - TextureCache[name] = texture; - texture.textureCacheId = name; + BaseTexture.addToCache(texture.baseTexture, name); + Texture.addToCache(texture, name); // also add references by url if they are different. if (name !== imageUrl) { - BaseTextureCache[imageUrl] = baseTexture; - TextureCache[imageUrl] = texture; + BaseTexture.addToCache(texture.baseTexture, imageUrl); + Texture.addToCache(texture, imageUrl); } return texture; } /** - * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object. + * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object. * * @static * @param {PIXI.Texture} texture - The Texture to add to the cache. - * @param {string} id - The id that the texture will be stored against. + * @param {string} id - The id that the Texture will be stored against. */ - static addTextureToCache(texture, id) + static addToCache(texture, id) { - if (!texture.textureCacheId) + if (id) { - texture.textureCacheId = id; + if (texture.textureCacheIds.indexOf(id) === -1) + { + texture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (TextureCache[id]) + { + console.warn(`Texture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + TextureCache[id] = texture; } - TextureCache[id] = texture; } /** - * Remove a texture from the global TextureCache. + * Remove a Texture from the global TextureCache. * * @static - * @param {string} id - The id of the texture to be removed - * @return {PIXI.Texture} The texture that was removed + * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself + * @return {PIXI.Texture|null} The Texture that was removed */ - static removeTextureFromCache(id) + static removeFromCache(texture) { - const texture = TextureCache[id]; + if (typeof texture === 'string') + { + const textureFromCache = TextureCache[texture]; - delete TextureCache[id]; - delete BaseTextureCache[id]; + if (textureFromCache) + { + const index = textureFromCache.textureCacheIds.indexOf(texture); - return texture; + if (index > -1) + { + textureFromCache.textureCacheIds.splice(index, 1); + } + + delete TextureCache[texture]; + + return textureFromCache; + } + } + else + { + for (let i = 0; i < texture.textureCacheIds.length; ++i) + { + delete TextureCache[texture.textureCacheIds[i]]; + } + + texture.textureCacheIds.length = 0; + + return texture; + } + + return null; } /** diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js index a9b9f51..65b74b9 100644 --- a/src/core/textures/VideoBaseTexture.js +++ b/src/core/textures/VideoBaseTexture.js @@ -193,7 +193,7 @@ if (this.source && this.source._pixiId) { - delete BaseTextureCache[this.source._pixiId]; + BaseTexture.removeFromCache(this.source._pixiId); delete this.source._pixiId; } @@ -220,7 +220,7 @@ if (!baseTexture) { baseTexture = new VideoBaseTexture(video, scaleMode); - BaseTextureCache[video._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, video._pixiId); } return baseTexture; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 582b24a..098b383 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -368,7 +368,7 @@ * @memberof PIXI.utils * @private */ -export const TextureCache = {}; +export const TextureCache = Object.create(null); /** * @todo Describe property usage @@ -376,7 +376,7 @@ * @memberof PIXI.utils * @private */ -export const BaseTextureCache = {}; +export const BaseTextureCache = Object.create(null); /** * Destroys all texture in the cache diff --git a/src/deprecation.js b/src/deprecation.js index 33daeec..e7fe5ce 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -830,6 +830,41 @@ warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;'); }; +/** + * @static + * @function + * @name PIXI.Texture.addTextureToCache + * @see PIXI.Texture.addToCache + * @deprecated since 4.5.0 + * @param {PIXI.Texture} texture - The Texture to add to the cache. + * @param {string} id - The id that the texture will be stored against. + */ +core.Texture.addTextureToCache = function addTextureToCache(texture, id) +{ + core.Texture.addToCache(texture, id); + warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.'); +}; + +/** + * @static + * @function + * @name PIXI.Texture.removeTextureFromCache + * @see PIXI.Texture.removeFromCache + * @deprecated since 4.5.0 + * @param {string} id - The id of the texture to be removed + * @return {PIXI.Texture|null} The texture that was removed + */ +core.Texture.removeTextureFromCache = function removeTextureFromCache(id) +{ + warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. ' + + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. ' + + 'For that, use BaseTexture.removeFromCache'); + + core.BaseTexture.removeFromCache(id); + + return core.Texture.removeFromCache(id); +}; + Object.defineProperties(filters, { /** diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 76c1079..9def67f 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1071,7 +1071,7 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode); + const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 1557cf8..065a4f5 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -42,13 +42,16 @@ canvas.width = 3; canvas.height = 3; - const texture = Texture.fromCanvas(canvas); + const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text'); texture.orig = new Rectangle(); texture.trim = new Rectangle(); super(texture); + // base texture is already automatically added to the cache, now adding the actual texture + Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]); + /** * The canvas element that everything is drawn to * diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 0a9259b..094ad8b 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -196,12 +196,6 @@ this._enabled = 0; this._virtalBoundId = -1; - // if no source passed don't try to load - if (source) - { - this.loadSource(source); - } - /** * If the object has been destroyed via destroy(). If true, it should not be used. * @@ -212,6 +206,21 @@ this._destroyed = false; /** + * The ids under which this BaseTexture has been added to the base texture cache. This is + * automatically set as long as BaseTexture.addToCache is used, but may not be set if a + * BaseTexture is added directly to the BaseTextureCache array. + * + * @member {string[]} + */ + this.textureCacheIds = []; + + // if no source passed don't try to load + if (source) + { + this.loadSource(source); + } + + /** * Fired when a not-immediately-available source finishes loading. * * @protected @@ -577,7 +586,7 @@ this.source = canvas; // Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`) - BaseTextureCache[canvas._pixiId] = this; + BaseTexture.addToCache(this, canvas._pixiId); this.isLoading = false; this._sourceLoaded(); @@ -618,13 +627,9 @@ this.dispose(); - for (const prop in BaseTextureCache) - { - if (BaseTextureCache[prop] === this) - { - delete BaseTextureCache[prop]; - } - } + BaseTexture.removeFromCache(this); + this.textureCacheIds = null; + this._destroyed = true; } @@ -692,7 +697,7 @@ image.src = imageUrl; // Setting this triggers load - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -704,13 +709,14 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.BaseTexture} The new base texture. */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { if (!canvas._pixiId) { - canvas._pixiId = `canvas_${uid()}`; + canvas._pixiId = `${origin}_${uid()}`; } let baseTexture = BaseTextureCache[canvas._pixiId]; @@ -718,7 +724,7 @@ if (!baseTexture) { baseTexture = new BaseTexture(canvas, scaleMode); - BaseTextureCache[canvas._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, canvas._pixiId); } return baseTexture; @@ -758,7 +764,7 @@ // if there is an @2x at the end of the url we are going to assume its a highres image baseTexture.resolution = getResolutionOfUrl(imageUrl); - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -771,4 +777,75 @@ // lets assume its a base texture! return source; } + + /** + * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object. + * + * @static + * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache. + * @param {string} id - The id that the BaseTexture will be stored against. + */ + static addToCache(baseTexture, id) + { + if (id) + { + if (baseTexture.textureCacheIds.indexOf(id) === -1) + { + baseTexture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (BaseTextureCache[id]) + { + console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + BaseTextureCache[id] = baseTexture; + } + } + + /** + * Remove a BaseTexture from the global BaseTextureCache. + * + * @static + * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself. + * @return {PIXI.BaseTexture|null} The BaseTexture that was removed. + */ + static removeFromCache(baseTexture) + { + if (typeof baseTexture === 'string') + { + const baseTextureFromCache = BaseTextureCache[baseTexture]; + + if (baseTextureFromCache) + { + const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture); + + if (index > -1) + { + baseTextureFromCache.textureCacheIds.splice(index, 1); + } + + delete BaseTextureCache[baseTexture]; + + return baseTextureFromCache; + } + } + else + { + for (let i = 0; i < baseTexture.textureCacheIds.length; ++i) + { + delete BaseTextureCache[baseTexture.textureCacheIds[i]]; + } + + baseTexture.textureCacheIds.length = 0; + + return baseTexture; + } + + return null; + } } diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 9b24f27..85abccf 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -1,5 +1,5 @@ import { Rectangle, Texture } from '../'; -import { getResolutionOfUrl, TextureCache } from '../utils'; +import { getResolutionOfUrl } from '../utils'; /** * Utility class for maintaining reference to a collection @@ -207,8 +207,7 @@ ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions - TextureCache[i] = this.textures[i]; - this.textures[i].textureCacheId = i; + Texture.addToCache(this.textures[i], i); } frameIndex++; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index bf2ae7b..6c4d324 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -3,7 +3,8 @@ import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; import { Rectangle } from '../math'; -import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils'; +import { TextureCache, getResolutionOfUrl } from '../utils'; +import settings from '../settings'; /** * A texture stores the information that represents an image or part of an image. It cannot be added @@ -158,13 +159,13 @@ this.transform = null; /** - * The id under which this Texture has been added to the texture cache. This is - * automatically set in certain cases, but may not always be accurate, particularly if - * the texture is in the cache under multiple ids. + * The ids under which this Texture has been added to the texture cache. This is + * automatically set as long as Texture.addToCache is used, but may not be set if a + * Texture is added directly to the TextureCache array. * - * @member {string} + * @member {string[]} */ - this.textureCacheId = null; + this.textureCacheIds = []; } /** @@ -231,7 +232,7 @@ // this only needs to be removed if the base texture is actually destroyed too.. if (TextureCache[this.baseTexture.imageUrl]) { - delete TextureCache[this.baseTexture.imageUrl]; + Texture.removeFromCache(this.baseTexture.imageUrl); } this.baseTexture.destroy(); @@ -247,17 +248,11 @@ this._uvs = null; this.trim = null; this.orig = null; - this.textureCacheId = null; this.valid = false; - for (const prop in TextureCache) - { - if (TextureCache[prop] === this) - { - delete TextureCache[prop]; - } - } + Texture.removeFromCache(this); + this.textureCacheIds = null; } /** @@ -305,7 +300,7 @@ if (!texture) { texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale)); - TextureCache[imageUrl] = texture; + Texture.addToCache(texture, imageUrl); } return texture; @@ -337,11 +332,12 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.Texture} The newly created texture */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { - return new Texture(BaseTexture.fromCanvas(canvas, scaleMode)); + return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin)); } /** @@ -413,7 +409,7 @@ } else if (source instanceof HTMLCanvasElement) { - return Texture.fromCanvas(source); + return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement'); } else if (source instanceof HTMLVideoElement) { @@ -452,51 +448,88 @@ } // lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions - BaseTextureCache[name] = baseTexture; - TextureCache[name] = texture; - texture.textureCacheId = name; + BaseTexture.addToCache(texture.baseTexture, name); + Texture.addToCache(texture, name); // also add references by url if they are different. if (name !== imageUrl) { - BaseTextureCache[imageUrl] = baseTexture; - TextureCache[imageUrl] = texture; + BaseTexture.addToCache(texture.baseTexture, imageUrl); + Texture.addToCache(texture, imageUrl); } return texture; } /** - * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object. + * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object. * * @static * @param {PIXI.Texture} texture - The Texture to add to the cache. - * @param {string} id - The id that the texture will be stored against. + * @param {string} id - The id that the Texture will be stored against. */ - static addTextureToCache(texture, id) + static addToCache(texture, id) { - if (!texture.textureCacheId) + if (id) { - texture.textureCacheId = id; + if (texture.textureCacheIds.indexOf(id) === -1) + { + texture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (TextureCache[id]) + { + console.warn(`Texture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + TextureCache[id] = texture; } - TextureCache[id] = texture; } /** - * Remove a texture from the global TextureCache. + * Remove a Texture from the global TextureCache. * * @static - * @param {string} id - The id of the texture to be removed - * @return {PIXI.Texture} The texture that was removed + * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself + * @return {PIXI.Texture|null} The Texture that was removed */ - static removeTextureFromCache(id) + static removeFromCache(texture) { - const texture = TextureCache[id]; + if (typeof texture === 'string') + { + const textureFromCache = TextureCache[texture]; - delete TextureCache[id]; - delete BaseTextureCache[id]; + if (textureFromCache) + { + const index = textureFromCache.textureCacheIds.indexOf(texture); - return texture; + if (index > -1) + { + textureFromCache.textureCacheIds.splice(index, 1); + } + + delete TextureCache[texture]; + + return textureFromCache; + } + } + else + { + for (let i = 0; i < texture.textureCacheIds.length; ++i) + { + delete TextureCache[texture.textureCacheIds[i]]; + } + + texture.textureCacheIds.length = 0; + + return texture; + } + + return null; } /** diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js index a9b9f51..65b74b9 100644 --- a/src/core/textures/VideoBaseTexture.js +++ b/src/core/textures/VideoBaseTexture.js @@ -193,7 +193,7 @@ if (this.source && this.source._pixiId) { - delete BaseTextureCache[this.source._pixiId]; + BaseTexture.removeFromCache(this.source._pixiId); delete this.source._pixiId; } @@ -220,7 +220,7 @@ if (!baseTexture) { baseTexture = new VideoBaseTexture(video, scaleMode); - BaseTextureCache[video._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, video._pixiId); } return baseTexture; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 582b24a..098b383 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -368,7 +368,7 @@ * @memberof PIXI.utils * @private */ -export const TextureCache = {}; +export const TextureCache = Object.create(null); /** * @todo Describe property usage @@ -376,7 +376,7 @@ * @memberof PIXI.utils * @private */ -export const BaseTextureCache = {}; +export const BaseTextureCache = Object.create(null); /** * Destroys all texture in the cache diff --git a/src/deprecation.js b/src/deprecation.js index 33daeec..e7fe5ce 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -830,6 +830,41 @@ warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;'); }; +/** + * @static + * @function + * @name PIXI.Texture.addTextureToCache + * @see PIXI.Texture.addToCache + * @deprecated since 4.5.0 + * @param {PIXI.Texture} texture - The Texture to add to the cache. + * @param {string} id - The id that the texture will be stored against. + */ +core.Texture.addTextureToCache = function addTextureToCache(texture, id) +{ + core.Texture.addToCache(texture, id); + warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.'); +}; + +/** + * @static + * @function + * @name PIXI.Texture.removeTextureFromCache + * @see PIXI.Texture.removeFromCache + * @deprecated since 4.5.0 + * @param {string} id - The id of the texture to be removed + * @return {PIXI.Texture|null} The texture that was removed + */ +core.Texture.removeTextureFromCache = function removeTextureFromCache(id) +{ + warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. ' + + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. ' + + 'For that, use BaseTexture.removeFromCache'); + + core.BaseTexture.removeFromCache(id); + + return core.Texture.removeFromCache(id); +}; + Object.defineProperties(filters, { /** diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js index aea06a8..4a165a3 100644 --- a/src/extras/cacheAsBitmap.js +++ b/src/extras/cacheAsBitmap.js @@ -1,4 +1,7 @@ import * as core from '../core'; +import Texture from '../core/textures/Texture'; +import BaseTexture from '../core/textures/BaseTexture'; +import { uid } from '../core/utils'; const DisplayObject = core.DisplayObject; const _tempMatrix = new core.Matrix(); @@ -20,6 +23,8 @@ */ constructor() { + this.textureCacheId = null; + this.originalRenderWebGL = null; this.originalRenderCanvas = null; this.originalCalculateBounds = null; @@ -185,6 +190,13 @@ const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0); + const textureCacheId = `cacheAsBitmap_${uid()}`; + + this._cacheData.textureCacheId = textureCacheId; + + BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId); + Texture.addToCache(renderTexture, textureCacheId); + // need to set // const m = _tempMatrix; @@ -289,6 +301,13 @@ const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0); + const textureCacheId = `cacheAsBitmap_${uid()}`; + + this._cacheData.textureCacheId = textureCacheId; + + BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId); + Texture.addToCache(renderTexture, textureCacheId); + // need to set // const m = _tempMatrix; @@ -371,6 +390,11 @@ { this._cacheData.sprite._texture.destroy(true); this._cacheData.sprite = null; + + BaseTexture.removeFromCache(this._cacheData.textureCacheId); + Texture.removeFromCache(this._cacheData.textureCacheId); + + this._cacheData.textureCacheId = null; }; /** diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 76c1079..9def67f 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1071,7 +1071,7 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode); + const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 1557cf8..065a4f5 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -42,13 +42,16 @@ canvas.width = 3; canvas.height = 3; - const texture = Texture.fromCanvas(canvas); + const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text'); texture.orig = new Rectangle(); texture.trim = new Rectangle(); super(texture); + // base texture is already automatically added to the cache, now adding the actual texture + Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]); + /** * The canvas element that everything is drawn to * diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 0a9259b..094ad8b 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -196,12 +196,6 @@ this._enabled = 0; this._virtalBoundId = -1; - // if no source passed don't try to load - if (source) - { - this.loadSource(source); - } - /** * If the object has been destroyed via destroy(). If true, it should not be used. * @@ -212,6 +206,21 @@ this._destroyed = false; /** + * The ids under which this BaseTexture has been added to the base texture cache. This is + * automatically set as long as BaseTexture.addToCache is used, but may not be set if a + * BaseTexture is added directly to the BaseTextureCache array. + * + * @member {string[]} + */ + this.textureCacheIds = []; + + // if no source passed don't try to load + if (source) + { + this.loadSource(source); + } + + /** * Fired when a not-immediately-available source finishes loading. * * @protected @@ -577,7 +586,7 @@ this.source = canvas; // Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`) - BaseTextureCache[canvas._pixiId] = this; + BaseTexture.addToCache(this, canvas._pixiId); this.isLoading = false; this._sourceLoaded(); @@ -618,13 +627,9 @@ this.dispose(); - for (const prop in BaseTextureCache) - { - if (BaseTextureCache[prop] === this) - { - delete BaseTextureCache[prop]; - } - } + BaseTexture.removeFromCache(this); + this.textureCacheIds = null; + this._destroyed = true; } @@ -692,7 +697,7 @@ image.src = imageUrl; // Setting this triggers load - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -704,13 +709,14 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.BaseTexture} The new base texture. */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { if (!canvas._pixiId) { - canvas._pixiId = `canvas_${uid()}`; + canvas._pixiId = `${origin}_${uid()}`; } let baseTexture = BaseTextureCache[canvas._pixiId]; @@ -718,7 +724,7 @@ if (!baseTexture) { baseTexture = new BaseTexture(canvas, scaleMode); - BaseTextureCache[canvas._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, canvas._pixiId); } return baseTexture; @@ -758,7 +764,7 @@ // if there is an @2x at the end of the url we are going to assume its a highres image baseTexture.resolution = getResolutionOfUrl(imageUrl); - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -771,4 +777,75 @@ // lets assume its a base texture! return source; } + + /** + * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object. + * + * @static + * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache. + * @param {string} id - The id that the BaseTexture will be stored against. + */ + static addToCache(baseTexture, id) + { + if (id) + { + if (baseTexture.textureCacheIds.indexOf(id) === -1) + { + baseTexture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (BaseTextureCache[id]) + { + console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + BaseTextureCache[id] = baseTexture; + } + } + + /** + * Remove a BaseTexture from the global BaseTextureCache. + * + * @static + * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself. + * @return {PIXI.BaseTexture|null} The BaseTexture that was removed. + */ + static removeFromCache(baseTexture) + { + if (typeof baseTexture === 'string') + { + const baseTextureFromCache = BaseTextureCache[baseTexture]; + + if (baseTextureFromCache) + { + const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture); + + if (index > -1) + { + baseTextureFromCache.textureCacheIds.splice(index, 1); + } + + delete BaseTextureCache[baseTexture]; + + return baseTextureFromCache; + } + } + else + { + for (let i = 0; i < baseTexture.textureCacheIds.length; ++i) + { + delete BaseTextureCache[baseTexture.textureCacheIds[i]]; + } + + baseTexture.textureCacheIds.length = 0; + + return baseTexture; + } + + return null; + } } diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 9b24f27..85abccf 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -1,5 +1,5 @@ import { Rectangle, Texture } from '../'; -import { getResolutionOfUrl, TextureCache } from '../utils'; +import { getResolutionOfUrl } from '../utils'; /** * Utility class for maintaining reference to a collection @@ -207,8 +207,7 @@ ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions - TextureCache[i] = this.textures[i]; - this.textures[i].textureCacheId = i; + Texture.addToCache(this.textures[i], i); } frameIndex++; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index bf2ae7b..6c4d324 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -3,7 +3,8 @@ import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; import { Rectangle } from '../math'; -import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils'; +import { TextureCache, getResolutionOfUrl } from '../utils'; +import settings from '../settings'; /** * A texture stores the information that represents an image or part of an image. It cannot be added @@ -158,13 +159,13 @@ this.transform = null; /** - * The id under which this Texture has been added to the texture cache. This is - * automatically set in certain cases, but may not always be accurate, particularly if - * the texture is in the cache under multiple ids. + * The ids under which this Texture has been added to the texture cache. This is + * automatically set as long as Texture.addToCache is used, but may not be set if a + * Texture is added directly to the TextureCache array. * - * @member {string} + * @member {string[]} */ - this.textureCacheId = null; + this.textureCacheIds = []; } /** @@ -231,7 +232,7 @@ // this only needs to be removed if the base texture is actually destroyed too.. if (TextureCache[this.baseTexture.imageUrl]) { - delete TextureCache[this.baseTexture.imageUrl]; + Texture.removeFromCache(this.baseTexture.imageUrl); } this.baseTexture.destroy(); @@ -247,17 +248,11 @@ this._uvs = null; this.trim = null; this.orig = null; - this.textureCacheId = null; this.valid = false; - for (const prop in TextureCache) - { - if (TextureCache[prop] === this) - { - delete TextureCache[prop]; - } - } + Texture.removeFromCache(this); + this.textureCacheIds = null; } /** @@ -305,7 +300,7 @@ if (!texture) { texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale)); - TextureCache[imageUrl] = texture; + Texture.addToCache(texture, imageUrl); } return texture; @@ -337,11 +332,12 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.Texture} The newly created texture */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { - return new Texture(BaseTexture.fromCanvas(canvas, scaleMode)); + return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin)); } /** @@ -413,7 +409,7 @@ } else if (source instanceof HTMLCanvasElement) { - return Texture.fromCanvas(source); + return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement'); } else if (source instanceof HTMLVideoElement) { @@ -452,51 +448,88 @@ } // lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions - BaseTextureCache[name] = baseTexture; - TextureCache[name] = texture; - texture.textureCacheId = name; + BaseTexture.addToCache(texture.baseTexture, name); + Texture.addToCache(texture, name); // also add references by url if they are different. if (name !== imageUrl) { - BaseTextureCache[imageUrl] = baseTexture; - TextureCache[imageUrl] = texture; + BaseTexture.addToCache(texture.baseTexture, imageUrl); + Texture.addToCache(texture, imageUrl); } return texture; } /** - * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object. + * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object. * * @static * @param {PIXI.Texture} texture - The Texture to add to the cache. - * @param {string} id - The id that the texture will be stored against. + * @param {string} id - The id that the Texture will be stored against. */ - static addTextureToCache(texture, id) + static addToCache(texture, id) { - if (!texture.textureCacheId) + if (id) { - texture.textureCacheId = id; + if (texture.textureCacheIds.indexOf(id) === -1) + { + texture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (TextureCache[id]) + { + console.warn(`Texture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + TextureCache[id] = texture; } - TextureCache[id] = texture; } /** - * Remove a texture from the global TextureCache. + * Remove a Texture from the global TextureCache. * * @static - * @param {string} id - The id of the texture to be removed - * @return {PIXI.Texture} The texture that was removed + * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself + * @return {PIXI.Texture|null} The Texture that was removed */ - static removeTextureFromCache(id) + static removeFromCache(texture) { - const texture = TextureCache[id]; + if (typeof texture === 'string') + { + const textureFromCache = TextureCache[texture]; - delete TextureCache[id]; - delete BaseTextureCache[id]; + if (textureFromCache) + { + const index = textureFromCache.textureCacheIds.indexOf(texture); - return texture; + if (index > -1) + { + textureFromCache.textureCacheIds.splice(index, 1); + } + + delete TextureCache[texture]; + + return textureFromCache; + } + } + else + { + for (let i = 0; i < texture.textureCacheIds.length; ++i) + { + delete TextureCache[texture.textureCacheIds[i]]; + } + + texture.textureCacheIds.length = 0; + + return texture; + } + + return null; } /** diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js index a9b9f51..65b74b9 100644 --- a/src/core/textures/VideoBaseTexture.js +++ b/src/core/textures/VideoBaseTexture.js @@ -193,7 +193,7 @@ if (this.source && this.source._pixiId) { - delete BaseTextureCache[this.source._pixiId]; + BaseTexture.removeFromCache(this.source._pixiId); delete this.source._pixiId; } @@ -220,7 +220,7 @@ if (!baseTexture) { baseTexture = new VideoBaseTexture(video, scaleMode); - BaseTextureCache[video._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, video._pixiId); } return baseTexture; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 582b24a..098b383 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -368,7 +368,7 @@ * @memberof PIXI.utils * @private */ -export const TextureCache = {}; +export const TextureCache = Object.create(null); /** * @todo Describe property usage @@ -376,7 +376,7 @@ * @memberof PIXI.utils * @private */ -export const BaseTextureCache = {}; +export const BaseTextureCache = Object.create(null); /** * Destroys all texture in the cache diff --git a/src/deprecation.js b/src/deprecation.js index 33daeec..e7fe5ce 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -830,6 +830,41 @@ warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;'); }; +/** + * @static + * @function + * @name PIXI.Texture.addTextureToCache + * @see PIXI.Texture.addToCache + * @deprecated since 4.5.0 + * @param {PIXI.Texture} texture - The Texture to add to the cache. + * @param {string} id - The id that the texture will be stored against. + */ +core.Texture.addTextureToCache = function addTextureToCache(texture, id) +{ + core.Texture.addToCache(texture, id); + warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.'); +}; + +/** + * @static + * @function + * @name PIXI.Texture.removeTextureFromCache + * @see PIXI.Texture.removeFromCache + * @deprecated since 4.5.0 + * @param {string} id - The id of the texture to be removed + * @return {PIXI.Texture|null} The texture that was removed + */ +core.Texture.removeTextureFromCache = function removeTextureFromCache(id) +{ + warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. ' + + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. ' + + 'For that, use BaseTexture.removeFromCache'); + + core.BaseTexture.removeFromCache(id); + + return core.Texture.removeFromCache(id); +}; + Object.defineProperties(filters, { /** diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js index aea06a8..4a165a3 100644 --- a/src/extras/cacheAsBitmap.js +++ b/src/extras/cacheAsBitmap.js @@ -1,4 +1,7 @@ import * as core from '../core'; +import Texture from '../core/textures/Texture'; +import BaseTexture from '../core/textures/BaseTexture'; +import { uid } from '../core/utils'; const DisplayObject = core.DisplayObject; const _tempMatrix = new core.Matrix(); @@ -20,6 +23,8 @@ */ constructor() { + this.textureCacheId = null; + this.originalRenderWebGL = null; this.originalRenderCanvas = null; this.originalCalculateBounds = null; @@ -185,6 +190,13 @@ const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0); + const textureCacheId = `cacheAsBitmap_${uid()}`; + + this._cacheData.textureCacheId = textureCacheId; + + BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId); + Texture.addToCache(renderTexture, textureCacheId); + // need to set // const m = _tempMatrix; @@ -289,6 +301,13 @@ const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0); + const textureCacheId = `cacheAsBitmap_${uid()}`; + + this._cacheData.textureCacheId = textureCacheId; + + BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId); + Texture.addToCache(renderTexture, textureCacheId); + // need to set // const m = _tempMatrix; @@ -371,6 +390,11 @@ { this._cacheData.sprite._texture.destroy(true); this._cacheData.sprite = null; + + BaseTexture.removeFromCache(this._cacheData.textureCacheId); + Texture.removeFromCache(this._cacheData.textureCacheId); + + this._cacheData.textureCacheId = null; }; /** diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js index bb65068..ac44c6a 100644 --- a/test/core/BaseTexture.js +++ b/test/core/BaseTexture.js @@ -1,11 +1,28 @@ 'use strict'; +const URL = 'foo.png'; +const NAME = 'foo'; +const NAME2 = 'bar'; + +function cleanCache() +{ + delete PIXI.utils.BaseTextureCache[URL]; + delete PIXI.utils.BaseTextureCache[NAME]; + delete PIXI.utils.BaseTextureCache[NAME2]; + + delete PIXI.utils.TextureCache[URL]; + delete PIXI.utils.TextureCache[NAME]; + delete PIXI.utils.TextureCache[NAME2]; +} + describe('BaseTexture', function () { describe('updateImageType', function () { it('should allow no extension', function () { + cleanCache(); + const baseTexture = new PIXI.BaseTexture(); baseTexture.imageUrl = 'http://some.domain.org/100/100'; @@ -17,25 +34,71 @@ it('should remove Canvas BaseTexture from cache on destroy', function () { + cleanCache(); + const canvas = document.createElement('canvas'); - const texture = PIXI.BaseTexture.fromCanvas(canvas); + const baseTexture = PIXI.BaseTexture.fromCanvas(canvas); const _pixiId = canvas._pixiId; - expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(texture); - texture.destroy(); + expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0); + expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture); + baseTexture.destroy(); + expect(baseTexture.textureCacheIds).to.equal(null); expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined); }); it('should remove Image BaseTexture from cache on destroy', function () { - const URL = 'foo.png'; - const NAME = 'bar'; + cleanCache(); + const image = new Image(); const texture = PIXI.Texture.fromLoader(image, URL, NAME); + expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1); expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture); texture.destroy(true); + expect(texture.baseTexture).to.equal(null); expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined); + }); + + it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function () + { + cleanCache(); + + const baseTexture = new PIXI.BaseTexture(); + + PIXI.BaseTexture.addToCache(baseTexture, NAME); + PIXI.BaseTexture.addToCache(baseTexture, NAME2); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); + PIXI.BaseTexture.removeFromCache(baseTexture); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined); + }); + + it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function () + { + cleanCache(); + + const baseTexture = new PIXI.BaseTexture(); + + PIXI.BaseTexture.addToCache(baseTexture, NAME); + PIXI.BaseTexture.addToCache(baseTexture, NAME2); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); + PIXI.BaseTexture.removeFromCache(NAME); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); }); }); diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 76c1079..9def67f 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1071,7 +1071,7 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode); + const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 1557cf8..065a4f5 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -42,13 +42,16 @@ canvas.width = 3; canvas.height = 3; - const texture = Texture.fromCanvas(canvas); + const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text'); texture.orig = new Rectangle(); texture.trim = new Rectangle(); super(texture); + // base texture is already automatically added to the cache, now adding the actual texture + Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]); + /** * The canvas element that everything is drawn to * diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 0a9259b..094ad8b 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -196,12 +196,6 @@ this._enabled = 0; this._virtalBoundId = -1; - // if no source passed don't try to load - if (source) - { - this.loadSource(source); - } - /** * If the object has been destroyed via destroy(). If true, it should not be used. * @@ -212,6 +206,21 @@ this._destroyed = false; /** + * The ids under which this BaseTexture has been added to the base texture cache. This is + * automatically set as long as BaseTexture.addToCache is used, but may not be set if a + * BaseTexture is added directly to the BaseTextureCache array. + * + * @member {string[]} + */ + this.textureCacheIds = []; + + // if no source passed don't try to load + if (source) + { + this.loadSource(source); + } + + /** * Fired when a not-immediately-available source finishes loading. * * @protected @@ -577,7 +586,7 @@ this.source = canvas; // Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`) - BaseTextureCache[canvas._pixiId] = this; + BaseTexture.addToCache(this, canvas._pixiId); this.isLoading = false; this._sourceLoaded(); @@ -618,13 +627,9 @@ this.dispose(); - for (const prop in BaseTextureCache) - { - if (BaseTextureCache[prop] === this) - { - delete BaseTextureCache[prop]; - } - } + BaseTexture.removeFromCache(this); + this.textureCacheIds = null; + this._destroyed = true; } @@ -692,7 +697,7 @@ image.src = imageUrl; // Setting this triggers load - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -704,13 +709,14 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.BaseTexture} The new base texture. */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { if (!canvas._pixiId) { - canvas._pixiId = `canvas_${uid()}`; + canvas._pixiId = `${origin}_${uid()}`; } let baseTexture = BaseTextureCache[canvas._pixiId]; @@ -718,7 +724,7 @@ if (!baseTexture) { baseTexture = new BaseTexture(canvas, scaleMode); - BaseTextureCache[canvas._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, canvas._pixiId); } return baseTexture; @@ -758,7 +764,7 @@ // if there is an @2x at the end of the url we are going to assume its a highres image baseTexture.resolution = getResolutionOfUrl(imageUrl); - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -771,4 +777,75 @@ // lets assume its a base texture! return source; } + + /** + * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object. + * + * @static + * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache. + * @param {string} id - The id that the BaseTexture will be stored against. + */ + static addToCache(baseTexture, id) + { + if (id) + { + if (baseTexture.textureCacheIds.indexOf(id) === -1) + { + baseTexture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (BaseTextureCache[id]) + { + console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + BaseTextureCache[id] = baseTexture; + } + } + + /** + * Remove a BaseTexture from the global BaseTextureCache. + * + * @static + * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself. + * @return {PIXI.BaseTexture|null} The BaseTexture that was removed. + */ + static removeFromCache(baseTexture) + { + if (typeof baseTexture === 'string') + { + const baseTextureFromCache = BaseTextureCache[baseTexture]; + + if (baseTextureFromCache) + { + const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture); + + if (index > -1) + { + baseTextureFromCache.textureCacheIds.splice(index, 1); + } + + delete BaseTextureCache[baseTexture]; + + return baseTextureFromCache; + } + } + else + { + for (let i = 0; i < baseTexture.textureCacheIds.length; ++i) + { + delete BaseTextureCache[baseTexture.textureCacheIds[i]]; + } + + baseTexture.textureCacheIds.length = 0; + + return baseTexture; + } + + return null; + } } diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 9b24f27..85abccf 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -1,5 +1,5 @@ import { Rectangle, Texture } from '../'; -import { getResolutionOfUrl, TextureCache } from '../utils'; +import { getResolutionOfUrl } from '../utils'; /** * Utility class for maintaining reference to a collection @@ -207,8 +207,7 @@ ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions - TextureCache[i] = this.textures[i]; - this.textures[i].textureCacheId = i; + Texture.addToCache(this.textures[i], i); } frameIndex++; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index bf2ae7b..6c4d324 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -3,7 +3,8 @@ import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; import { Rectangle } from '../math'; -import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils'; +import { TextureCache, getResolutionOfUrl } from '../utils'; +import settings from '../settings'; /** * A texture stores the information that represents an image or part of an image. It cannot be added @@ -158,13 +159,13 @@ this.transform = null; /** - * The id under which this Texture has been added to the texture cache. This is - * automatically set in certain cases, but may not always be accurate, particularly if - * the texture is in the cache under multiple ids. + * The ids under which this Texture has been added to the texture cache. This is + * automatically set as long as Texture.addToCache is used, but may not be set if a + * Texture is added directly to the TextureCache array. * - * @member {string} + * @member {string[]} */ - this.textureCacheId = null; + this.textureCacheIds = []; } /** @@ -231,7 +232,7 @@ // this only needs to be removed if the base texture is actually destroyed too.. if (TextureCache[this.baseTexture.imageUrl]) { - delete TextureCache[this.baseTexture.imageUrl]; + Texture.removeFromCache(this.baseTexture.imageUrl); } this.baseTexture.destroy(); @@ -247,17 +248,11 @@ this._uvs = null; this.trim = null; this.orig = null; - this.textureCacheId = null; this.valid = false; - for (const prop in TextureCache) - { - if (TextureCache[prop] === this) - { - delete TextureCache[prop]; - } - } + Texture.removeFromCache(this); + this.textureCacheIds = null; } /** @@ -305,7 +300,7 @@ if (!texture) { texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale)); - TextureCache[imageUrl] = texture; + Texture.addToCache(texture, imageUrl); } return texture; @@ -337,11 +332,12 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.Texture} The newly created texture */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { - return new Texture(BaseTexture.fromCanvas(canvas, scaleMode)); + return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin)); } /** @@ -413,7 +409,7 @@ } else if (source instanceof HTMLCanvasElement) { - return Texture.fromCanvas(source); + return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement'); } else if (source instanceof HTMLVideoElement) { @@ -452,51 +448,88 @@ } // lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions - BaseTextureCache[name] = baseTexture; - TextureCache[name] = texture; - texture.textureCacheId = name; + BaseTexture.addToCache(texture.baseTexture, name); + Texture.addToCache(texture, name); // also add references by url if they are different. if (name !== imageUrl) { - BaseTextureCache[imageUrl] = baseTexture; - TextureCache[imageUrl] = texture; + BaseTexture.addToCache(texture.baseTexture, imageUrl); + Texture.addToCache(texture, imageUrl); } return texture; } /** - * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object. + * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object. * * @static * @param {PIXI.Texture} texture - The Texture to add to the cache. - * @param {string} id - The id that the texture will be stored against. + * @param {string} id - The id that the Texture will be stored against. */ - static addTextureToCache(texture, id) + static addToCache(texture, id) { - if (!texture.textureCacheId) + if (id) { - texture.textureCacheId = id; + if (texture.textureCacheIds.indexOf(id) === -1) + { + texture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (TextureCache[id]) + { + console.warn(`Texture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + TextureCache[id] = texture; } - TextureCache[id] = texture; } /** - * Remove a texture from the global TextureCache. + * Remove a Texture from the global TextureCache. * * @static - * @param {string} id - The id of the texture to be removed - * @return {PIXI.Texture} The texture that was removed + * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself + * @return {PIXI.Texture|null} The Texture that was removed */ - static removeTextureFromCache(id) + static removeFromCache(texture) { - const texture = TextureCache[id]; + if (typeof texture === 'string') + { + const textureFromCache = TextureCache[texture]; - delete TextureCache[id]; - delete BaseTextureCache[id]; + if (textureFromCache) + { + const index = textureFromCache.textureCacheIds.indexOf(texture); - return texture; + if (index > -1) + { + textureFromCache.textureCacheIds.splice(index, 1); + } + + delete TextureCache[texture]; + + return textureFromCache; + } + } + else + { + for (let i = 0; i < texture.textureCacheIds.length; ++i) + { + delete TextureCache[texture.textureCacheIds[i]]; + } + + texture.textureCacheIds.length = 0; + + return texture; + } + + return null; } /** diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js index a9b9f51..65b74b9 100644 --- a/src/core/textures/VideoBaseTexture.js +++ b/src/core/textures/VideoBaseTexture.js @@ -193,7 +193,7 @@ if (this.source && this.source._pixiId) { - delete BaseTextureCache[this.source._pixiId]; + BaseTexture.removeFromCache(this.source._pixiId); delete this.source._pixiId; } @@ -220,7 +220,7 @@ if (!baseTexture) { baseTexture = new VideoBaseTexture(video, scaleMode); - BaseTextureCache[video._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, video._pixiId); } return baseTexture; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 582b24a..098b383 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -368,7 +368,7 @@ * @memberof PIXI.utils * @private */ -export const TextureCache = {}; +export const TextureCache = Object.create(null); /** * @todo Describe property usage @@ -376,7 +376,7 @@ * @memberof PIXI.utils * @private */ -export const BaseTextureCache = {}; +export const BaseTextureCache = Object.create(null); /** * Destroys all texture in the cache diff --git a/src/deprecation.js b/src/deprecation.js index 33daeec..e7fe5ce 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -830,6 +830,41 @@ warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;'); }; +/** + * @static + * @function + * @name PIXI.Texture.addTextureToCache + * @see PIXI.Texture.addToCache + * @deprecated since 4.5.0 + * @param {PIXI.Texture} texture - The Texture to add to the cache. + * @param {string} id - The id that the texture will be stored against. + */ +core.Texture.addTextureToCache = function addTextureToCache(texture, id) +{ + core.Texture.addToCache(texture, id); + warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.'); +}; + +/** + * @static + * @function + * @name PIXI.Texture.removeTextureFromCache + * @see PIXI.Texture.removeFromCache + * @deprecated since 4.5.0 + * @param {string} id - The id of the texture to be removed + * @return {PIXI.Texture|null} The texture that was removed + */ +core.Texture.removeTextureFromCache = function removeTextureFromCache(id) +{ + warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. ' + + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. ' + + 'For that, use BaseTexture.removeFromCache'); + + core.BaseTexture.removeFromCache(id); + + return core.Texture.removeFromCache(id); +}; + Object.defineProperties(filters, { /** diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js index aea06a8..4a165a3 100644 --- a/src/extras/cacheAsBitmap.js +++ b/src/extras/cacheAsBitmap.js @@ -1,4 +1,7 @@ import * as core from '../core'; +import Texture from '../core/textures/Texture'; +import BaseTexture from '../core/textures/BaseTexture'; +import { uid } from '../core/utils'; const DisplayObject = core.DisplayObject; const _tempMatrix = new core.Matrix(); @@ -20,6 +23,8 @@ */ constructor() { + this.textureCacheId = null; + this.originalRenderWebGL = null; this.originalRenderCanvas = null; this.originalCalculateBounds = null; @@ -185,6 +190,13 @@ const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0); + const textureCacheId = `cacheAsBitmap_${uid()}`; + + this._cacheData.textureCacheId = textureCacheId; + + BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId); + Texture.addToCache(renderTexture, textureCacheId); + // need to set // const m = _tempMatrix; @@ -289,6 +301,13 @@ const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0); + const textureCacheId = `cacheAsBitmap_${uid()}`; + + this._cacheData.textureCacheId = textureCacheId; + + BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId); + Texture.addToCache(renderTexture, textureCacheId); + // need to set // const m = _tempMatrix; @@ -371,6 +390,11 @@ { this._cacheData.sprite._texture.destroy(true); this._cacheData.sprite = null; + + BaseTexture.removeFromCache(this._cacheData.textureCacheId); + Texture.removeFromCache(this._cacheData.textureCacheId); + + this._cacheData.textureCacheId = null; }; /** diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js index bb65068..ac44c6a 100644 --- a/test/core/BaseTexture.js +++ b/test/core/BaseTexture.js @@ -1,11 +1,28 @@ 'use strict'; +const URL = 'foo.png'; +const NAME = 'foo'; +const NAME2 = 'bar'; + +function cleanCache() +{ + delete PIXI.utils.BaseTextureCache[URL]; + delete PIXI.utils.BaseTextureCache[NAME]; + delete PIXI.utils.BaseTextureCache[NAME2]; + + delete PIXI.utils.TextureCache[URL]; + delete PIXI.utils.TextureCache[NAME]; + delete PIXI.utils.TextureCache[NAME2]; +} + describe('BaseTexture', function () { describe('updateImageType', function () { it('should allow no extension', function () { + cleanCache(); + const baseTexture = new PIXI.BaseTexture(); baseTexture.imageUrl = 'http://some.domain.org/100/100'; @@ -17,25 +34,71 @@ it('should remove Canvas BaseTexture from cache on destroy', function () { + cleanCache(); + const canvas = document.createElement('canvas'); - const texture = PIXI.BaseTexture.fromCanvas(canvas); + const baseTexture = PIXI.BaseTexture.fromCanvas(canvas); const _pixiId = canvas._pixiId; - expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(texture); - texture.destroy(); + expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0); + expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture); + baseTexture.destroy(); + expect(baseTexture.textureCacheIds).to.equal(null); expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined); }); it('should remove Image BaseTexture from cache on destroy', function () { - const URL = 'foo.png'; - const NAME = 'bar'; + cleanCache(); + const image = new Image(); const texture = PIXI.Texture.fromLoader(image, URL, NAME); + expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1); expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture); texture.destroy(true); + expect(texture.baseTexture).to.equal(null); expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined); + }); + + it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function () + { + cleanCache(); + + const baseTexture = new PIXI.BaseTexture(); + + PIXI.BaseTexture.addToCache(baseTexture, NAME); + PIXI.BaseTexture.addToCache(baseTexture, NAME2); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); + PIXI.BaseTexture.removeFromCache(baseTexture); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined); + }); + + it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function () + { + cleanCache(); + + const baseTexture = new PIXI.BaseTexture(); + + PIXI.BaseTexture.addToCache(baseTexture, NAME); + PIXI.BaseTexture.addToCache(baseTexture, NAME2); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); + PIXI.BaseTexture.removeFromCache(NAME); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); }); }); diff --git a/test/core/Bounds.js b/test/core/Bounds.js index d2893a6..454a47c 100644 --- a/test/core/Bounds.js +++ b/test/core/Bounds.js @@ -350,7 +350,7 @@ expect(bounds.height).to.equal(20); }); - it('should register correct width/height with an a Text Object', function () + it('should register correct width/height with a Text Object', function () { const parent = new PIXI.Container(); diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 76c1079..9def67f 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1071,7 +1071,7 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode); + const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 1557cf8..065a4f5 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -42,13 +42,16 @@ canvas.width = 3; canvas.height = 3; - const texture = Texture.fromCanvas(canvas); + const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text'); texture.orig = new Rectangle(); texture.trim = new Rectangle(); super(texture); + // base texture is already automatically added to the cache, now adding the actual texture + Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]); + /** * The canvas element that everything is drawn to * diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 0a9259b..094ad8b 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -196,12 +196,6 @@ this._enabled = 0; this._virtalBoundId = -1; - // if no source passed don't try to load - if (source) - { - this.loadSource(source); - } - /** * If the object has been destroyed via destroy(). If true, it should not be used. * @@ -212,6 +206,21 @@ this._destroyed = false; /** + * The ids under which this BaseTexture has been added to the base texture cache. This is + * automatically set as long as BaseTexture.addToCache is used, but may not be set if a + * BaseTexture is added directly to the BaseTextureCache array. + * + * @member {string[]} + */ + this.textureCacheIds = []; + + // if no source passed don't try to load + if (source) + { + this.loadSource(source); + } + + /** * Fired when a not-immediately-available source finishes loading. * * @protected @@ -577,7 +586,7 @@ this.source = canvas; // Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`) - BaseTextureCache[canvas._pixiId] = this; + BaseTexture.addToCache(this, canvas._pixiId); this.isLoading = false; this._sourceLoaded(); @@ -618,13 +627,9 @@ this.dispose(); - for (const prop in BaseTextureCache) - { - if (BaseTextureCache[prop] === this) - { - delete BaseTextureCache[prop]; - } - } + BaseTexture.removeFromCache(this); + this.textureCacheIds = null; + this._destroyed = true; } @@ -692,7 +697,7 @@ image.src = imageUrl; // Setting this triggers load - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -704,13 +709,14 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.BaseTexture} The new base texture. */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { if (!canvas._pixiId) { - canvas._pixiId = `canvas_${uid()}`; + canvas._pixiId = `${origin}_${uid()}`; } let baseTexture = BaseTextureCache[canvas._pixiId]; @@ -718,7 +724,7 @@ if (!baseTexture) { baseTexture = new BaseTexture(canvas, scaleMode); - BaseTextureCache[canvas._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, canvas._pixiId); } return baseTexture; @@ -758,7 +764,7 @@ // if there is an @2x at the end of the url we are going to assume its a highres image baseTexture.resolution = getResolutionOfUrl(imageUrl); - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -771,4 +777,75 @@ // lets assume its a base texture! return source; } + + /** + * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object. + * + * @static + * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache. + * @param {string} id - The id that the BaseTexture will be stored against. + */ + static addToCache(baseTexture, id) + { + if (id) + { + if (baseTexture.textureCacheIds.indexOf(id) === -1) + { + baseTexture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (BaseTextureCache[id]) + { + console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + BaseTextureCache[id] = baseTexture; + } + } + + /** + * Remove a BaseTexture from the global BaseTextureCache. + * + * @static + * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself. + * @return {PIXI.BaseTexture|null} The BaseTexture that was removed. + */ + static removeFromCache(baseTexture) + { + if (typeof baseTexture === 'string') + { + const baseTextureFromCache = BaseTextureCache[baseTexture]; + + if (baseTextureFromCache) + { + const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture); + + if (index > -1) + { + baseTextureFromCache.textureCacheIds.splice(index, 1); + } + + delete BaseTextureCache[baseTexture]; + + return baseTextureFromCache; + } + } + else + { + for (let i = 0; i < baseTexture.textureCacheIds.length; ++i) + { + delete BaseTextureCache[baseTexture.textureCacheIds[i]]; + } + + baseTexture.textureCacheIds.length = 0; + + return baseTexture; + } + + return null; + } } diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 9b24f27..85abccf 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -1,5 +1,5 @@ import { Rectangle, Texture } from '../'; -import { getResolutionOfUrl, TextureCache } from '../utils'; +import { getResolutionOfUrl } from '../utils'; /** * Utility class for maintaining reference to a collection @@ -207,8 +207,7 @@ ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions - TextureCache[i] = this.textures[i]; - this.textures[i].textureCacheId = i; + Texture.addToCache(this.textures[i], i); } frameIndex++; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index bf2ae7b..6c4d324 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -3,7 +3,8 @@ import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; import { Rectangle } from '../math'; -import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils'; +import { TextureCache, getResolutionOfUrl } from '../utils'; +import settings from '../settings'; /** * A texture stores the information that represents an image or part of an image. It cannot be added @@ -158,13 +159,13 @@ this.transform = null; /** - * The id under which this Texture has been added to the texture cache. This is - * automatically set in certain cases, but may not always be accurate, particularly if - * the texture is in the cache under multiple ids. + * The ids under which this Texture has been added to the texture cache. This is + * automatically set as long as Texture.addToCache is used, but may not be set if a + * Texture is added directly to the TextureCache array. * - * @member {string} + * @member {string[]} */ - this.textureCacheId = null; + this.textureCacheIds = []; } /** @@ -231,7 +232,7 @@ // this only needs to be removed if the base texture is actually destroyed too.. if (TextureCache[this.baseTexture.imageUrl]) { - delete TextureCache[this.baseTexture.imageUrl]; + Texture.removeFromCache(this.baseTexture.imageUrl); } this.baseTexture.destroy(); @@ -247,17 +248,11 @@ this._uvs = null; this.trim = null; this.orig = null; - this.textureCacheId = null; this.valid = false; - for (const prop in TextureCache) - { - if (TextureCache[prop] === this) - { - delete TextureCache[prop]; - } - } + Texture.removeFromCache(this); + this.textureCacheIds = null; } /** @@ -305,7 +300,7 @@ if (!texture) { texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale)); - TextureCache[imageUrl] = texture; + Texture.addToCache(texture, imageUrl); } return texture; @@ -337,11 +332,12 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.Texture} The newly created texture */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { - return new Texture(BaseTexture.fromCanvas(canvas, scaleMode)); + return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin)); } /** @@ -413,7 +409,7 @@ } else if (source instanceof HTMLCanvasElement) { - return Texture.fromCanvas(source); + return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement'); } else if (source instanceof HTMLVideoElement) { @@ -452,51 +448,88 @@ } // lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions - BaseTextureCache[name] = baseTexture; - TextureCache[name] = texture; - texture.textureCacheId = name; + BaseTexture.addToCache(texture.baseTexture, name); + Texture.addToCache(texture, name); // also add references by url if they are different. if (name !== imageUrl) { - BaseTextureCache[imageUrl] = baseTexture; - TextureCache[imageUrl] = texture; + BaseTexture.addToCache(texture.baseTexture, imageUrl); + Texture.addToCache(texture, imageUrl); } return texture; } /** - * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object. + * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object. * * @static * @param {PIXI.Texture} texture - The Texture to add to the cache. - * @param {string} id - The id that the texture will be stored against. + * @param {string} id - The id that the Texture will be stored against. */ - static addTextureToCache(texture, id) + static addToCache(texture, id) { - if (!texture.textureCacheId) + if (id) { - texture.textureCacheId = id; + if (texture.textureCacheIds.indexOf(id) === -1) + { + texture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (TextureCache[id]) + { + console.warn(`Texture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + TextureCache[id] = texture; } - TextureCache[id] = texture; } /** - * Remove a texture from the global TextureCache. + * Remove a Texture from the global TextureCache. * * @static - * @param {string} id - The id of the texture to be removed - * @return {PIXI.Texture} The texture that was removed + * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself + * @return {PIXI.Texture|null} The Texture that was removed */ - static removeTextureFromCache(id) + static removeFromCache(texture) { - const texture = TextureCache[id]; + if (typeof texture === 'string') + { + const textureFromCache = TextureCache[texture]; - delete TextureCache[id]; - delete BaseTextureCache[id]; + if (textureFromCache) + { + const index = textureFromCache.textureCacheIds.indexOf(texture); - return texture; + if (index > -1) + { + textureFromCache.textureCacheIds.splice(index, 1); + } + + delete TextureCache[texture]; + + return textureFromCache; + } + } + else + { + for (let i = 0; i < texture.textureCacheIds.length; ++i) + { + delete TextureCache[texture.textureCacheIds[i]]; + } + + texture.textureCacheIds.length = 0; + + return texture; + } + + return null; } /** diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js index a9b9f51..65b74b9 100644 --- a/src/core/textures/VideoBaseTexture.js +++ b/src/core/textures/VideoBaseTexture.js @@ -193,7 +193,7 @@ if (this.source && this.source._pixiId) { - delete BaseTextureCache[this.source._pixiId]; + BaseTexture.removeFromCache(this.source._pixiId); delete this.source._pixiId; } @@ -220,7 +220,7 @@ if (!baseTexture) { baseTexture = new VideoBaseTexture(video, scaleMode); - BaseTextureCache[video._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, video._pixiId); } return baseTexture; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 582b24a..098b383 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -368,7 +368,7 @@ * @memberof PIXI.utils * @private */ -export const TextureCache = {}; +export const TextureCache = Object.create(null); /** * @todo Describe property usage @@ -376,7 +376,7 @@ * @memberof PIXI.utils * @private */ -export const BaseTextureCache = {}; +export const BaseTextureCache = Object.create(null); /** * Destroys all texture in the cache diff --git a/src/deprecation.js b/src/deprecation.js index 33daeec..e7fe5ce 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -830,6 +830,41 @@ warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;'); }; +/** + * @static + * @function + * @name PIXI.Texture.addTextureToCache + * @see PIXI.Texture.addToCache + * @deprecated since 4.5.0 + * @param {PIXI.Texture} texture - The Texture to add to the cache. + * @param {string} id - The id that the texture will be stored against. + */ +core.Texture.addTextureToCache = function addTextureToCache(texture, id) +{ + core.Texture.addToCache(texture, id); + warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.'); +}; + +/** + * @static + * @function + * @name PIXI.Texture.removeTextureFromCache + * @see PIXI.Texture.removeFromCache + * @deprecated since 4.5.0 + * @param {string} id - The id of the texture to be removed + * @return {PIXI.Texture|null} The texture that was removed + */ +core.Texture.removeTextureFromCache = function removeTextureFromCache(id) +{ + warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. ' + + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. ' + + 'For that, use BaseTexture.removeFromCache'); + + core.BaseTexture.removeFromCache(id); + + return core.Texture.removeFromCache(id); +}; + Object.defineProperties(filters, { /** diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js index aea06a8..4a165a3 100644 --- a/src/extras/cacheAsBitmap.js +++ b/src/extras/cacheAsBitmap.js @@ -1,4 +1,7 @@ import * as core from '../core'; +import Texture from '../core/textures/Texture'; +import BaseTexture from '../core/textures/BaseTexture'; +import { uid } from '../core/utils'; const DisplayObject = core.DisplayObject; const _tempMatrix = new core.Matrix(); @@ -20,6 +23,8 @@ */ constructor() { + this.textureCacheId = null; + this.originalRenderWebGL = null; this.originalRenderCanvas = null; this.originalCalculateBounds = null; @@ -185,6 +190,13 @@ const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0); + const textureCacheId = `cacheAsBitmap_${uid()}`; + + this._cacheData.textureCacheId = textureCacheId; + + BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId); + Texture.addToCache(renderTexture, textureCacheId); + // need to set // const m = _tempMatrix; @@ -289,6 +301,13 @@ const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0); + const textureCacheId = `cacheAsBitmap_${uid()}`; + + this._cacheData.textureCacheId = textureCacheId; + + BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId); + Texture.addToCache(renderTexture, textureCacheId); + // need to set // const m = _tempMatrix; @@ -371,6 +390,11 @@ { this._cacheData.sprite._texture.destroy(true); this._cacheData.sprite = null; + + BaseTexture.removeFromCache(this._cacheData.textureCacheId); + Texture.removeFromCache(this._cacheData.textureCacheId); + + this._cacheData.textureCacheId = null; }; /** diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js index bb65068..ac44c6a 100644 --- a/test/core/BaseTexture.js +++ b/test/core/BaseTexture.js @@ -1,11 +1,28 @@ 'use strict'; +const URL = 'foo.png'; +const NAME = 'foo'; +const NAME2 = 'bar'; + +function cleanCache() +{ + delete PIXI.utils.BaseTextureCache[URL]; + delete PIXI.utils.BaseTextureCache[NAME]; + delete PIXI.utils.BaseTextureCache[NAME2]; + + delete PIXI.utils.TextureCache[URL]; + delete PIXI.utils.TextureCache[NAME]; + delete PIXI.utils.TextureCache[NAME2]; +} + describe('BaseTexture', function () { describe('updateImageType', function () { it('should allow no extension', function () { + cleanCache(); + const baseTexture = new PIXI.BaseTexture(); baseTexture.imageUrl = 'http://some.domain.org/100/100'; @@ -17,25 +34,71 @@ it('should remove Canvas BaseTexture from cache on destroy', function () { + cleanCache(); + const canvas = document.createElement('canvas'); - const texture = PIXI.BaseTexture.fromCanvas(canvas); + const baseTexture = PIXI.BaseTexture.fromCanvas(canvas); const _pixiId = canvas._pixiId; - expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(texture); - texture.destroy(); + expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0); + expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture); + baseTexture.destroy(); + expect(baseTexture.textureCacheIds).to.equal(null); expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined); }); it('should remove Image BaseTexture from cache on destroy', function () { - const URL = 'foo.png'; - const NAME = 'bar'; + cleanCache(); + const image = new Image(); const texture = PIXI.Texture.fromLoader(image, URL, NAME); + expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1); expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture); texture.destroy(true); + expect(texture.baseTexture).to.equal(null); expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined); + }); + + it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function () + { + cleanCache(); + + const baseTexture = new PIXI.BaseTexture(); + + PIXI.BaseTexture.addToCache(baseTexture, NAME); + PIXI.BaseTexture.addToCache(baseTexture, NAME2); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); + PIXI.BaseTexture.removeFromCache(baseTexture); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined); + }); + + it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function () + { + cleanCache(); + + const baseTexture = new PIXI.BaseTexture(); + + PIXI.BaseTexture.addToCache(baseTexture, NAME); + PIXI.BaseTexture.addToCache(baseTexture, NAME2); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); + PIXI.BaseTexture.removeFromCache(NAME); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); }); }); diff --git a/test/core/Bounds.js b/test/core/Bounds.js index d2893a6..454a47c 100644 --- a/test/core/Bounds.js +++ b/test/core/Bounds.js @@ -350,7 +350,7 @@ expect(bounds.height).to.equal(20); }); - it('should register correct width/height with an a Text Object', function () + it('should register correct width/height with a Text Object', function () { const parent = new PIXI.Container(); diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 7601a0c..7a3d6ac 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -18,7 +18,7 @@ expect(textures[id]).to.be.an.instanceof(PIXI.Texture); expect(textures[id].width).to.equal(spritesheet.data.frames[id].frame.w / spritesheet.resolution); expect(textures[id].height).to.equal(spritesheet.data.frames[id].frame.h / spritesheet.resolution); - expect(textures[id].textureCacheId).to.equal(id); + expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; expect(spritesheet.baseTexture).to.be.null; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 76c1079..9def67f 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1071,7 +1071,7 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode); + const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 1557cf8..065a4f5 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -42,13 +42,16 @@ canvas.width = 3; canvas.height = 3; - const texture = Texture.fromCanvas(canvas); + const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text'); texture.orig = new Rectangle(); texture.trim = new Rectangle(); super(texture); + // base texture is already automatically added to the cache, now adding the actual texture + Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]); + /** * The canvas element that everything is drawn to * diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 0a9259b..094ad8b 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -196,12 +196,6 @@ this._enabled = 0; this._virtalBoundId = -1; - // if no source passed don't try to load - if (source) - { - this.loadSource(source); - } - /** * If the object has been destroyed via destroy(). If true, it should not be used. * @@ -212,6 +206,21 @@ this._destroyed = false; /** + * The ids under which this BaseTexture has been added to the base texture cache. This is + * automatically set as long as BaseTexture.addToCache is used, but may not be set if a + * BaseTexture is added directly to the BaseTextureCache array. + * + * @member {string[]} + */ + this.textureCacheIds = []; + + // if no source passed don't try to load + if (source) + { + this.loadSource(source); + } + + /** * Fired when a not-immediately-available source finishes loading. * * @protected @@ -577,7 +586,7 @@ this.source = canvas; // Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`) - BaseTextureCache[canvas._pixiId] = this; + BaseTexture.addToCache(this, canvas._pixiId); this.isLoading = false; this._sourceLoaded(); @@ -618,13 +627,9 @@ this.dispose(); - for (const prop in BaseTextureCache) - { - if (BaseTextureCache[prop] === this) - { - delete BaseTextureCache[prop]; - } - } + BaseTexture.removeFromCache(this); + this.textureCacheIds = null; + this._destroyed = true; } @@ -692,7 +697,7 @@ image.src = imageUrl; // Setting this triggers load - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -704,13 +709,14 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.BaseTexture} The new base texture. */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { if (!canvas._pixiId) { - canvas._pixiId = `canvas_${uid()}`; + canvas._pixiId = `${origin}_${uid()}`; } let baseTexture = BaseTextureCache[canvas._pixiId]; @@ -718,7 +724,7 @@ if (!baseTexture) { baseTexture = new BaseTexture(canvas, scaleMode); - BaseTextureCache[canvas._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, canvas._pixiId); } return baseTexture; @@ -758,7 +764,7 @@ // if there is an @2x at the end of the url we are going to assume its a highres image baseTexture.resolution = getResolutionOfUrl(imageUrl); - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -771,4 +777,75 @@ // lets assume its a base texture! return source; } + + /** + * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object. + * + * @static + * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache. + * @param {string} id - The id that the BaseTexture will be stored against. + */ + static addToCache(baseTexture, id) + { + if (id) + { + if (baseTexture.textureCacheIds.indexOf(id) === -1) + { + baseTexture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (BaseTextureCache[id]) + { + console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + BaseTextureCache[id] = baseTexture; + } + } + + /** + * Remove a BaseTexture from the global BaseTextureCache. + * + * @static + * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself. + * @return {PIXI.BaseTexture|null} The BaseTexture that was removed. + */ + static removeFromCache(baseTexture) + { + if (typeof baseTexture === 'string') + { + const baseTextureFromCache = BaseTextureCache[baseTexture]; + + if (baseTextureFromCache) + { + const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture); + + if (index > -1) + { + baseTextureFromCache.textureCacheIds.splice(index, 1); + } + + delete BaseTextureCache[baseTexture]; + + return baseTextureFromCache; + } + } + else + { + for (let i = 0; i < baseTexture.textureCacheIds.length; ++i) + { + delete BaseTextureCache[baseTexture.textureCacheIds[i]]; + } + + baseTexture.textureCacheIds.length = 0; + + return baseTexture; + } + + return null; + } } diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 9b24f27..85abccf 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -1,5 +1,5 @@ import { Rectangle, Texture } from '../'; -import { getResolutionOfUrl, TextureCache } from '../utils'; +import { getResolutionOfUrl } from '../utils'; /** * Utility class for maintaining reference to a collection @@ -207,8 +207,7 @@ ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions - TextureCache[i] = this.textures[i]; - this.textures[i].textureCacheId = i; + Texture.addToCache(this.textures[i], i); } frameIndex++; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index bf2ae7b..6c4d324 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -3,7 +3,8 @@ import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; import { Rectangle } from '../math'; -import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils'; +import { TextureCache, getResolutionOfUrl } from '../utils'; +import settings from '../settings'; /** * A texture stores the information that represents an image or part of an image. It cannot be added @@ -158,13 +159,13 @@ this.transform = null; /** - * The id under which this Texture has been added to the texture cache. This is - * automatically set in certain cases, but may not always be accurate, particularly if - * the texture is in the cache under multiple ids. + * The ids under which this Texture has been added to the texture cache. This is + * automatically set as long as Texture.addToCache is used, but may not be set if a + * Texture is added directly to the TextureCache array. * - * @member {string} + * @member {string[]} */ - this.textureCacheId = null; + this.textureCacheIds = []; } /** @@ -231,7 +232,7 @@ // this only needs to be removed if the base texture is actually destroyed too.. if (TextureCache[this.baseTexture.imageUrl]) { - delete TextureCache[this.baseTexture.imageUrl]; + Texture.removeFromCache(this.baseTexture.imageUrl); } this.baseTexture.destroy(); @@ -247,17 +248,11 @@ this._uvs = null; this.trim = null; this.orig = null; - this.textureCacheId = null; this.valid = false; - for (const prop in TextureCache) - { - if (TextureCache[prop] === this) - { - delete TextureCache[prop]; - } - } + Texture.removeFromCache(this); + this.textureCacheIds = null; } /** @@ -305,7 +300,7 @@ if (!texture) { texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale)); - TextureCache[imageUrl] = texture; + Texture.addToCache(texture, imageUrl); } return texture; @@ -337,11 +332,12 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.Texture} The newly created texture */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { - return new Texture(BaseTexture.fromCanvas(canvas, scaleMode)); + return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin)); } /** @@ -413,7 +409,7 @@ } else if (source instanceof HTMLCanvasElement) { - return Texture.fromCanvas(source); + return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement'); } else if (source instanceof HTMLVideoElement) { @@ -452,51 +448,88 @@ } // lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions - BaseTextureCache[name] = baseTexture; - TextureCache[name] = texture; - texture.textureCacheId = name; + BaseTexture.addToCache(texture.baseTexture, name); + Texture.addToCache(texture, name); // also add references by url if they are different. if (name !== imageUrl) { - BaseTextureCache[imageUrl] = baseTexture; - TextureCache[imageUrl] = texture; + BaseTexture.addToCache(texture.baseTexture, imageUrl); + Texture.addToCache(texture, imageUrl); } return texture; } /** - * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object. + * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object. * * @static * @param {PIXI.Texture} texture - The Texture to add to the cache. - * @param {string} id - The id that the texture will be stored against. + * @param {string} id - The id that the Texture will be stored against. */ - static addTextureToCache(texture, id) + static addToCache(texture, id) { - if (!texture.textureCacheId) + if (id) { - texture.textureCacheId = id; + if (texture.textureCacheIds.indexOf(id) === -1) + { + texture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (TextureCache[id]) + { + console.warn(`Texture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + TextureCache[id] = texture; } - TextureCache[id] = texture; } /** - * Remove a texture from the global TextureCache. + * Remove a Texture from the global TextureCache. * * @static - * @param {string} id - The id of the texture to be removed - * @return {PIXI.Texture} The texture that was removed + * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself + * @return {PIXI.Texture|null} The Texture that was removed */ - static removeTextureFromCache(id) + static removeFromCache(texture) { - const texture = TextureCache[id]; + if (typeof texture === 'string') + { + const textureFromCache = TextureCache[texture]; - delete TextureCache[id]; - delete BaseTextureCache[id]; + if (textureFromCache) + { + const index = textureFromCache.textureCacheIds.indexOf(texture); - return texture; + if (index > -1) + { + textureFromCache.textureCacheIds.splice(index, 1); + } + + delete TextureCache[texture]; + + return textureFromCache; + } + } + else + { + for (let i = 0; i < texture.textureCacheIds.length; ++i) + { + delete TextureCache[texture.textureCacheIds[i]]; + } + + texture.textureCacheIds.length = 0; + + return texture; + } + + return null; } /** diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js index a9b9f51..65b74b9 100644 --- a/src/core/textures/VideoBaseTexture.js +++ b/src/core/textures/VideoBaseTexture.js @@ -193,7 +193,7 @@ if (this.source && this.source._pixiId) { - delete BaseTextureCache[this.source._pixiId]; + BaseTexture.removeFromCache(this.source._pixiId); delete this.source._pixiId; } @@ -220,7 +220,7 @@ if (!baseTexture) { baseTexture = new VideoBaseTexture(video, scaleMode); - BaseTextureCache[video._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, video._pixiId); } return baseTexture; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 582b24a..098b383 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -368,7 +368,7 @@ * @memberof PIXI.utils * @private */ -export const TextureCache = {}; +export const TextureCache = Object.create(null); /** * @todo Describe property usage @@ -376,7 +376,7 @@ * @memberof PIXI.utils * @private */ -export const BaseTextureCache = {}; +export const BaseTextureCache = Object.create(null); /** * Destroys all texture in the cache diff --git a/src/deprecation.js b/src/deprecation.js index 33daeec..e7fe5ce 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -830,6 +830,41 @@ warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;'); }; +/** + * @static + * @function + * @name PIXI.Texture.addTextureToCache + * @see PIXI.Texture.addToCache + * @deprecated since 4.5.0 + * @param {PIXI.Texture} texture - The Texture to add to the cache. + * @param {string} id - The id that the texture will be stored against. + */ +core.Texture.addTextureToCache = function addTextureToCache(texture, id) +{ + core.Texture.addToCache(texture, id); + warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.'); +}; + +/** + * @static + * @function + * @name PIXI.Texture.removeTextureFromCache + * @see PIXI.Texture.removeFromCache + * @deprecated since 4.5.0 + * @param {string} id - The id of the texture to be removed + * @return {PIXI.Texture|null} The texture that was removed + */ +core.Texture.removeTextureFromCache = function removeTextureFromCache(id) +{ + warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. ' + + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. ' + + 'For that, use BaseTexture.removeFromCache'); + + core.BaseTexture.removeFromCache(id); + + return core.Texture.removeFromCache(id); +}; + Object.defineProperties(filters, { /** diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js index aea06a8..4a165a3 100644 --- a/src/extras/cacheAsBitmap.js +++ b/src/extras/cacheAsBitmap.js @@ -1,4 +1,7 @@ import * as core from '../core'; +import Texture from '../core/textures/Texture'; +import BaseTexture from '../core/textures/BaseTexture'; +import { uid } from '../core/utils'; const DisplayObject = core.DisplayObject; const _tempMatrix = new core.Matrix(); @@ -20,6 +23,8 @@ */ constructor() { + this.textureCacheId = null; + this.originalRenderWebGL = null; this.originalRenderCanvas = null; this.originalCalculateBounds = null; @@ -185,6 +190,13 @@ const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0); + const textureCacheId = `cacheAsBitmap_${uid()}`; + + this._cacheData.textureCacheId = textureCacheId; + + BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId); + Texture.addToCache(renderTexture, textureCacheId); + // need to set // const m = _tempMatrix; @@ -289,6 +301,13 @@ const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0); + const textureCacheId = `cacheAsBitmap_${uid()}`; + + this._cacheData.textureCacheId = textureCacheId; + + BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId); + Texture.addToCache(renderTexture, textureCacheId); + // need to set // const m = _tempMatrix; @@ -371,6 +390,11 @@ { this._cacheData.sprite._texture.destroy(true); this._cacheData.sprite = null; + + BaseTexture.removeFromCache(this._cacheData.textureCacheId); + Texture.removeFromCache(this._cacheData.textureCacheId); + + this._cacheData.textureCacheId = null; }; /** diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js index bb65068..ac44c6a 100644 --- a/test/core/BaseTexture.js +++ b/test/core/BaseTexture.js @@ -1,11 +1,28 @@ 'use strict'; +const URL = 'foo.png'; +const NAME = 'foo'; +const NAME2 = 'bar'; + +function cleanCache() +{ + delete PIXI.utils.BaseTextureCache[URL]; + delete PIXI.utils.BaseTextureCache[NAME]; + delete PIXI.utils.BaseTextureCache[NAME2]; + + delete PIXI.utils.TextureCache[URL]; + delete PIXI.utils.TextureCache[NAME]; + delete PIXI.utils.TextureCache[NAME2]; +} + describe('BaseTexture', function () { describe('updateImageType', function () { it('should allow no extension', function () { + cleanCache(); + const baseTexture = new PIXI.BaseTexture(); baseTexture.imageUrl = 'http://some.domain.org/100/100'; @@ -17,25 +34,71 @@ it('should remove Canvas BaseTexture from cache on destroy', function () { + cleanCache(); + const canvas = document.createElement('canvas'); - const texture = PIXI.BaseTexture.fromCanvas(canvas); + const baseTexture = PIXI.BaseTexture.fromCanvas(canvas); const _pixiId = canvas._pixiId; - expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(texture); - texture.destroy(); + expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0); + expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture); + baseTexture.destroy(); + expect(baseTexture.textureCacheIds).to.equal(null); expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined); }); it('should remove Image BaseTexture from cache on destroy', function () { - const URL = 'foo.png'; - const NAME = 'bar'; + cleanCache(); + const image = new Image(); const texture = PIXI.Texture.fromLoader(image, URL, NAME); + expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1); expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture); texture.destroy(true); + expect(texture.baseTexture).to.equal(null); expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined); + }); + + it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function () + { + cleanCache(); + + const baseTexture = new PIXI.BaseTexture(); + + PIXI.BaseTexture.addToCache(baseTexture, NAME); + PIXI.BaseTexture.addToCache(baseTexture, NAME2); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); + PIXI.BaseTexture.removeFromCache(baseTexture); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined); + }); + + it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function () + { + cleanCache(); + + const baseTexture = new PIXI.BaseTexture(); + + PIXI.BaseTexture.addToCache(baseTexture, NAME); + PIXI.BaseTexture.addToCache(baseTexture, NAME2); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); + PIXI.BaseTexture.removeFromCache(NAME); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); }); }); diff --git a/test/core/Bounds.js b/test/core/Bounds.js index d2893a6..454a47c 100644 --- a/test/core/Bounds.js +++ b/test/core/Bounds.js @@ -350,7 +350,7 @@ expect(bounds.height).to.equal(20); }); - it('should register correct width/height with an a Text Object', function () + it('should register correct width/height with a Text Object', function () { const parent = new PIXI.Container(); diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 7601a0c..7a3d6ac 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -18,7 +18,7 @@ expect(textures[id]).to.be.an.instanceof(PIXI.Texture); expect(textures[id].width).to.equal(spritesheet.data.frames[id].frame.w / spritesheet.resolution); expect(textures[id].height).to.equal(spritesheet.data.frames[id].frame.h / spritesheet.resolution); - expect(textures[id].textureCacheId).to.equal(id); + expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; expect(spritesheet.baseTexture).to.be.null; diff --git a/test/core/Texture.js b/test/core/Texture.js index 241ec3e..995ed40 100644 --- a/test/core/Texture.js +++ b/test/core/Texture.js @@ -1,11 +1,26 @@ 'use strict'; +const URL = 'foo.png'; +const NAME = 'foo'; +const NAME2 = 'bar'; + +function cleanCache() +{ + delete PIXI.utils.BaseTextureCache[URL]; + delete PIXI.utils.BaseTextureCache[NAME]; + delete PIXI.utils.BaseTextureCache[NAME2]; + + delete PIXI.utils.TextureCache[URL]; + delete PIXI.utils.TextureCache[NAME]; + delete PIXI.utils.TextureCache[NAME2]; +} + describe('PIXI.Texture', function () { it('should register Texture from Loader', function () { - const URL = 'foo.png'; - const NAME = 'bar'; + cleanCache(); + const image = new Image(); const texture = PIXI.Texture.fromLoader(image, URL, NAME); @@ -19,17 +34,97 @@ it('should remove Texture from cache on destroy', function () { - const NAME = 'foo'; - const NAME2 = 'bar'; + cleanCache(); + const texture = new PIXI.Texture(new PIXI.BaseTexture()); - PIXI.Texture.addTextureToCache(texture, NAME); - PIXI.Texture.addTextureToCache(texture, NAME2); - expect(texture.textureCacheId).to.equal(NAME); + PIXI.Texture.addToCache(texture, NAME); + PIXI.Texture.addToCache(texture, NAME2); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1); expect(PIXI.utils.TextureCache[NAME]).to.equal(texture); expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture); texture.destroy(); + expect(texture.textureCacheIds).to.equal(null); expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined); expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined); }); + + it('should be added to the texture cache correctly, ' + + 'and should remove only itself, not effecting the base texture and its cache', function () + { + cleanCache(); + + const texture = new PIXI.Texture(new PIXI.BaseTexture()); + + PIXI.BaseTexture.addToCache(texture.baseTexture, NAME); + PIXI.Texture.addToCache(texture, NAME); + expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture); + expect(PIXI.utils.TextureCache[NAME]).to.equal(texture); + PIXI.Texture.removeFromCache(NAME); + expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture); + expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined); + }); + + it('should be added to the texture cache correctly using legacy addTextureToCache, ' + + 'and should remove also remove the base texture from its cache with removeTextureFromCache', function () + { + cleanCache(); + + const texture = new PIXI.Texture(new PIXI.BaseTexture()); + + PIXI.BaseTexture.addToCache(texture.baseTexture, NAME); + PIXI.Texture.addTextureToCache(texture, NAME); + expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture); + expect(PIXI.utils.TextureCache[NAME]).to.equal(texture); + PIXI.Texture.removeTextureFromCache(NAME); + expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined); + }); + + it('should remove Texture from entire cache using removeFromCache (by Texture instance)', function () + { + cleanCache(); + + const texture = new PIXI.Texture(new PIXI.BaseTexture()); + + PIXI.Texture.addToCache(texture, NAME); + PIXI.Texture.addToCache(texture, NAME2); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1); + expect(PIXI.utils.TextureCache[NAME]).to.equal(texture); + expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture); + PIXI.Texture.removeFromCache(texture); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(-1); + expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined); + }); + + it('should remove Texture from single cache entry using removeFromCache (by id)', function () + { + cleanCache(); + + const texture = new PIXI.Texture(new PIXI.BaseTexture()); + + PIXI.Texture.addToCache(texture, NAME); + PIXI.Texture.addToCache(texture, NAME2); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1); + expect(PIXI.utils.TextureCache[NAME]).to.equal(texture); + expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture); + PIXI.Texture.removeFromCache(NAME); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(0); + expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture); + }); }); diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 76c1079..9def67f 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -1071,7 +1071,7 @@ canvasRenderer.render(this, canvasBuffer, true, tempMatrix); - const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode); + const texture = Texture.fromCanvas(canvasBuffer.baseTexture._canvasRenderTarget.canvas, scaleMode, 'graphics'); texture.baseTexture.resolution = resolution; texture.baseTexture.update(); diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 1557cf8..065a4f5 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -42,13 +42,16 @@ canvas.width = 3; canvas.height = 3; - const texture = Texture.fromCanvas(canvas); + const texture = Texture.fromCanvas(canvas, settings.SCALE_MODE, 'text'); texture.orig = new Rectangle(); texture.trim = new Rectangle(); super(texture); + // base texture is already automatically added to the cache, now adding the actual texture + Texture.addToCache(this._texture, this._texture.baseTexture.textureCacheIds[0]); + /** * The canvas element that everything is drawn to * diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 0a9259b..094ad8b 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -196,12 +196,6 @@ this._enabled = 0; this._virtalBoundId = -1; - // if no source passed don't try to load - if (source) - { - this.loadSource(source); - } - /** * If the object has been destroyed via destroy(). If true, it should not be used. * @@ -212,6 +206,21 @@ this._destroyed = false; /** + * The ids under which this BaseTexture has been added to the base texture cache. This is + * automatically set as long as BaseTexture.addToCache is used, but may not be set if a + * BaseTexture is added directly to the BaseTextureCache array. + * + * @member {string[]} + */ + this.textureCacheIds = []; + + // if no source passed don't try to load + if (source) + { + this.loadSource(source); + } + + /** * Fired when a not-immediately-available source finishes loading. * * @protected @@ -577,7 +586,7 @@ this.source = canvas; // Add also the canvas in cache (destroy clears by `imageUrl` and `source._pixiId`) - BaseTextureCache[canvas._pixiId] = this; + BaseTexture.addToCache(this, canvas._pixiId); this.isLoading = false; this._sourceLoaded(); @@ -618,13 +627,9 @@ this.dispose(); - for (const prop in BaseTextureCache) - { - if (BaseTextureCache[prop] === this) - { - delete BaseTextureCache[prop]; - } - } + BaseTexture.removeFromCache(this); + this.textureCacheIds = null; + this._destroyed = true; } @@ -692,7 +697,7 @@ image.src = imageUrl; // Setting this triggers load - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -704,13 +709,14 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} scaleMode - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.BaseTexture} The new base texture. */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { if (!canvas._pixiId) { - canvas._pixiId = `canvas_${uid()}`; + canvas._pixiId = `${origin}_${uid()}`; } let baseTexture = BaseTextureCache[canvas._pixiId]; @@ -718,7 +724,7 @@ if (!baseTexture) { baseTexture = new BaseTexture(canvas, scaleMode); - BaseTextureCache[canvas._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, canvas._pixiId); } return baseTexture; @@ -758,7 +764,7 @@ // if there is an @2x at the end of the url we are going to assume its a highres image baseTexture.resolution = getResolutionOfUrl(imageUrl); - BaseTextureCache[imageUrl] = baseTexture; + BaseTexture.addToCache(baseTexture, imageUrl); } return baseTexture; @@ -771,4 +777,75 @@ // lets assume its a base texture! return source; } + + /** + * Adds a BaseTexture to the global BaseTextureCache. This cache is shared across the whole PIXI object. + * + * @static + * @param {PIXI.BaseTexture} baseTexture - The BaseTexture to add to the cache. + * @param {string} id - The id that the BaseTexture will be stored against. + */ + static addToCache(baseTexture, id) + { + if (id) + { + if (baseTexture.textureCacheIds.indexOf(id) === -1) + { + baseTexture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (BaseTextureCache[id]) + { + console.warn(`BaseTexture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + BaseTextureCache[id] = baseTexture; + } + } + + /** + * Remove a BaseTexture from the global BaseTextureCache. + * + * @static + * @param {string|PIXI.BaseTexture} baseTexture - id of a BaseTexture to be removed, or a BaseTexture instance itself. + * @return {PIXI.BaseTexture|null} The BaseTexture that was removed. + */ + static removeFromCache(baseTexture) + { + if (typeof baseTexture === 'string') + { + const baseTextureFromCache = BaseTextureCache[baseTexture]; + + if (baseTextureFromCache) + { + const index = baseTextureFromCache.textureCacheIds.indexOf(baseTexture); + + if (index > -1) + { + baseTextureFromCache.textureCacheIds.splice(index, 1); + } + + delete BaseTextureCache[baseTexture]; + + return baseTextureFromCache; + } + } + else + { + for (let i = 0; i < baseTexture.textureCacheIds.length; ++i) + { + delete BaseTextureCache[baseTexture.textureCacheIds[i]]; + } + + baseTexture.textureCacheIds.length = 0; + + return baseTexture; + } + + return null; + } } diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index 9b24f27..85abccf 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -1,5 +1,5 @@ import { Rectangle, Texture } from '../'; -import { getResolutionOfUrl, TextureCache } from '../utils'; +import { getResolutionOfUrl } from '../utils'; /** * Utility class for maintaining reference to a collection @@ -207,8 +207,7 @@ ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions - TextureCache[i] = this.textures[i]; - this.textures[i].textureCacheId = i; + Texture.addToCache(this.textures[i], i); } frameIndex++; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index bf2ae7b..6c4d324 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -3,7 +3,8 @@ import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; import { Rectangle } from '../math'; -import { TextureCache, BaseTextureCache, getResolutionOfUrl } from '../utils'; +import { TextureCache, getResolutionOfUrl } from '../utils'; +import settings from '../settings'; /** * A texture stores the information that represents an image or part of an image. It cannot be added @@ -158,13 +159,13 @@ this.transform = null; /** - * The id under which this Texture has been added to the texture cache. This is - * automatically set in certain cases, but may not always be accurate, particularly if - * the texture is in the cache under multiple ids. + * The ids under which this Texture has been added to the texture cache. This is + * automatically set as long as Texture.addToCache is used, but may not be set if a + * Texture is added directly to the TextureCache array. * - * @member {string} + * @member {string[]} */ - this.textureCacheId = null; + this.textureCacheIds = []; } /** @@ -231,7 +232,7 @@ // this only needs to be removed if the base texture is actually destroyed too.. if (TextureCache[this.baseTexture.imageUrl]) { - delete TextureCache[this.baseTexture.imageUrl]; + Texture.removeFromCache(this.baseTexture.imageUrl); } this.baseTexture.destroy(); @@ -247,17 +248,11 @@ this._uvs = null; this.trim = null; this.orig = null; - this.textureCacheId = null; this.valid = false; - for (const prop in TextureCache) - { - if (TextureCache[prop] === this) - { - delete TextureCache[prop]; - } - } + Texture.removeFromCache(this); + this.textureCacheIds = null; } /** @@ -305,7 +300,7 @@ if (!texture) { texture = new Texture(BaseTexture.fromImage(imageUrl, crossorigin, scaleMode, sourceScale)); - TextureCache[imageUrl] = texture; + Texture.addToCache(texture, imageUrl); } return texture; @@ -337,11 +332,12 @@ * @static * @param {HTMLCanvasElement} canvas - The canvas element source of the texture * @param {number} [scaleMode=PIXI.settings.SCALE_MODE] - See {@link PIXI.SCALE_MODES} for possible values + * @param {string} [origin='canvas'] - A string origin of who created the base texture * @return {PIXI.Texture} The newly created texture */ - static fromCanvas(canvas, scaleMode) + static fromCanvas(canvas, scaleMode, origin = 'canvas') { - return new Texture(BaseTexture.fromCanvas(canvas, scaleMode)); + return new Texture(BaseTexture.fromCanvas(canvas, scaleMode, origin)); } /** @@ -413,7 +409,7 @@ } else if (source instanceof HTMLCanvasElement) { - return Texture.fromCanvas(source); + return Texture.fromCanvas(source, settings.SCALE_MODE, 'HTMLCanvasElement'); } else if (source instanceof HTMLVideoElement) { @@ -452,51 +448,88 @@ } // lets also add the frame to pixi's global cache for fromFrame and fromImage fucntions - BaseTextureCache[name] = baseTexture; - TextureCache[name] = texture; - texture.textureCacheId = name; + BaseTexture.addToCache(texture.baseTexture, name); + Texture.addToCache(texture, name); // also add references by url if they are different. if (name !== imageUrl) { - BaseTextureCache[imageUrl] = baseTexture; - TextureCache[imageUrl] = texture; + BaseTexture.addToCache(texture.baseTexture, imageUrl); + Texture.addToCache(texture, imageUrl); } return texture; } /** - * Adds a texture to the global TextureCache. This cache is shared across the whole PIXI object. + * Adds a Texture to the global TextureCache. This cache is shared across the whole PIXI object. * * @static * @param {PIXI.Texture} texture - The Texture to add to the cache. - * @param {string} id - The id that the texture will be stored against. + * @param {string} id - The id that the Texture will be stored against. */ - static addTextureToCache(texture, id) + static addToCache(texture, id) { - if (!texture.textureCacheId) + if (id) { - texture.textureCacheId = id; + if (texture.textureCacheIds.indexOf(id) === -1) + { + texture.textureCacheIds.push(id); + } + + // @if DEBUG + /* eslint-disable no-console */ + if (TextureCache[id]) + { + console.warn(`Texture added to the cache with an id [${id}] that already had an entry`); + } + /* eslint-enable no-console */ + // @endif + + TextureCache[id] = texture; } - TextureCache[id] = texture; } /** - * Remove a texture from the global TextureCache. + * Remove a Texture from the global TextureCache. * * @static - * @param {string} id - The id of the texture to be removed - * @return {PIXI.Texture} The texture that was removed + * @param {string|PIXI.Texture} texture - id of a Texture to be removed, or a Texture instance itself + * @return {PIXI.Texture|null} The Texture that was removed */ - static removeTextureFromCache(id) + static removeFromCache(texture) { - const texture = TextureCache[id]; + if (typeof texture === 'string') + { + const textureFromCache = TextureCache[texture]; - delete TextureCache[id]; - delete BaseTextureCache[id]; + if (textureFromCache) + { + const index = textureFromCache.textureCacheIds.indexOf(texture); - return texture; + if (index > -1) + { + textureFromCache.textureCacheIds.splice(index, 1); + } + + delete TextureCache[texture]; + + return textureFromCache; + } + } + else + { + for (let i = 0; i < texture.textureCacheIds.length; ++i) + { + delete TextureCache[texture.textureCacheIds[i]]; + } + + texture.textureCacheIds.length = 0; + + return texture; + } + + return null; } /** diff --git a/src/core/textures/VideoBaseTexture.js b/src/core/textures/VideoBaseTexture.js index a9b9f51..65b74b9 100644 --- a/src/core/textures/VideoBaseTexture.js +++ b/src/core/textures/VideoBaseTexture.js @@ -193,7 +193,7 @@ if (this.source && this.source._pixiId) { - delete BaseTextureCache[this.source._pixiId]; + BaseTexture.removeFromCache(this.source._pixiId); delete this.source._pixiId; } @@ -220,7 +220,7 @@ if (!baseTexture) { baseTexture = new VideoBaseTexture(video, scaleMode); - BaseTextureCache[video._pixiId] = baseTexture; + BaseTexture.addToCache(baseTexture, video._pixiId); } return baseTexture; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 582b24a..098b383 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -368,7 +368,7 @@ * @memberof PIXI.utils * @private */ -export const TextureCache = {}; +export const TextureCache = Object.create(null); /** * @todo Describe property usage @@ -376,7 +376,7 @@ * @memberof PIXI.utils * @private */ -export const BaseTextureCache = {}; +export const BaseTextureCache = Object.create(null); /** * Destroys all texture in the cache diff --git a/src/deprecation.js b/src/deprecation.js index 33daeec..e7fe5ce 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -830,6 +830,41 @@ warn('setFrame is now deprecated, please use the frame property, e.g: myTexture.frame = frame;'); }; +/** + * @static + * @function + * @name PIXI.Texture.addTextureToCache + * @see PIXI.Texture.addToCache + * @deprecated since 4.5.0 + * @param {PIXI.Texture} texture - The Texture to add to the cache. + * @param {string} id - The id that the texture will be stored against. + */ +core.Texture.addTextureToCache = function addTextureToCache(texture, id) +{ + core.Texture.addToCache(texture, id); + warn('Texture.addTextureToCache is deprecated, please use Texture.addToCache from now on.'); +}; + +/** + * @static + * @function + * @name PIXI.Texture.removeTextureFromCache + * @see PIXI.Texture.removeFromCache + * @deprecated since 4.5.0 + * @param {string} id - The id of the texture to be removed + * @return {PIXI.Texture|null} The texture that was removed + */ +core.Texture.removeTextureFromCache = function removeTextureFromCache(id) +{ + warn('Texture.removeTextureFromCache is deprecated, please use Texture.removeFromCache from now on. ' + + 'Be aware that Texture.removeFromCache does not automatically its BaseTexture from the BaseTextureCache. ' + + 'For that, use BaseTexture.removeFromCache'); + + core.BaseTexture.removeFromCache(id); + + return core.Texture.removeFromCache(id); +}; + Object.defineProperties(filters, { /** diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js index aea06a8..4a165a3 100644 --- a/src/extras/cacheAsBitmap.js +++ b/src/extras/cacheAsBitmap.js @@ -1,4 +1,7 @@ import * as core from '../core'; +import Texture from '../core/textures/Texture'; +import BaseTexture from '../core/textures/BaseTexture'; +import { uid } from '../core/utils'; const DisplayObject = core.DisplayObject; const _tempMatrix = new core.Matrix(); @@ -20,6 +23,8 @@ */ constructor() { + this.textureCacheId = null; + this.originalRenderWebGL = null; this.originalRenderCanvas = null; this.originalCalculateBounds = null; @@ -185,6 +190,13 @@ const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0); + const textureCacheId = `cacheAsBitmap_${uid()}`; + + this._cacheData.textureCacheId = textureCacheId; + + BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId); + Texture.addToCache(renderTexture, textureCacheId); + // need to set // const m = _tempMatrix; @@ -289,6 +301,13 @@ const renderTexture = core.RenderTexture.create(bounds.width | 0, bounds.height | 0); + const textureCacheId = `cacheAsBitmap_${uid()}`; + + this._cacheData.textureCacheId = textureCacheId; + + BaseTexture.addToCache(renderTexture.baseTexture, textureCacheId); + Texture.addToCache(renderTexture, textureCacheId); + // need to set // const m = _tempMatrix; @@ -371,6 +390,11 @@ { this._cacheData.sprite._texture.destroy(true); this._cacheData.sprite = null; + + BaseTexture.removeFromCache(this._cacheData.textureCacheId); + Texture.removeFromCache(this._cacheData.textureCacheId); + + this._cacheData.textureCacheId = null; }; /** diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js index bb65068..ac44c6a 100644 --- a/test/core/BaseTexture.js +++ b/test/core/BaseTexture.js @@ -1,11 +1,28 @@ 'use strict'; +const URL = 'foo.png'; +const NAME = 'foo'; +const NAME2 = 'bar'; + +function cleanCache() +{ + delete PIXI.utils.BaseTextureCache[URL]; + delete PIXI.utils.BaseTextureCache[NAME]; + delete PIXI.utils.BaseTextureCache[NAME2]; + + delete PIXI.utils.TextureCache[URL]; + delete PIXI.utils.TextureCache[NAME]; + delete PIXI.utils.TextureCache[NAME2]; +} + describe('BaseTexture', function () { describe('updateImageType', function () { it('should allow no extension', function () { + cleanCache(); + const baseTexture = new PIXI.BaseTexture(); baseTexture.imageUrl = 'http://some.domain.org/100/100'; @@ -17,25 +34,71 @@ it('should remove Canvas BaseTexture from cache on destroy', function () { + cleanCache(); + const canvas = document.createElement('canvas'); - const texture = PIXI.BaseTexture.fromCanvas(canvas); + const baseTexture = PIXI.BaseTexture.fromCanvas(canvas); const _pixiId = canvas._pixiId; - expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(texture); - texture.destroy(); + expect(baseTexture.textureCacheIds.indexOf(_pixiId)).to.equal(0); + expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(baseTexture); + baseTexture.destroy(); + expect(baseTexture.textureCacheIds).to.equal(null); expect(PIXI.utils.BaseTextureCache[_pixiId]).to.equal(undefined); }); it('should remove Image BaseTexture from cache on destroy', function () { - const URL = 'foo.png'; - const NAME = 'bar'; + cleanCache(); + const image = new Image(); const texture = PIXI.Texture.fromLoader(image, URL, NAME); + expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.baseTexture.textureCacheIds.indexOf(URL)).to.equal(1); expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture); texture.destroy(true); + expect(texture.baseTexture).to.equal(null); expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.BaseTextureCache[URL]).to.equal(undefined); + }); + + it('should remove BaseTexture from entire cache using removeFromCache (by BaseTexture instance)', function () + { + cleanCache(); + + const baseTexture = new PIXI.BaseTexture(); + + PIXI.BaseTexture.addToCache(baseTexture, NAME); + PIXI.BaseTexture.addToCache(baseTexture, NAME2); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); + PIXI.BaseTexture.removeFromCache(baseTexture); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(-1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(undefined); + }); + + it('should remove BaseTexture from single cache entry using removeFromCache (by id)', function () + { + cleanCache(); + + const baseTexture = new PIXI.BaseTexture(); + + PIXI.BaseTexture.addToCache(baseTexture, NAME); + PIXI.BaseTexture.addToCache(baseTexture, NAME2); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(baseTexture); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); + PIXI.BaseTexture.removeFromCache(NAME); + expect(baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(baseTexture.textureCacheIds.indexOf(NAME2)).to.equal(0); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); }); }); diff --git a/test/core/Bounds.js b/test/core/Bounds.js index d2893a6..454a47c 100644 --- a/test/core/Bounds.js +++ b/test/core/Bounds.js @@ -350,7 +350,7 @@ expect(bounds.height).to.equal(20); }); - it('should register correct width/height with an a Text Object', function () + it('should register correct width/height with a Text Object', function () { const parent = new PIXI.Container(); diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 7601a0c..7a3d6ac 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -18,7 +18,7 @@ expect(textures[id]).to.be.an.instanceof(PIXI.Texture); expect(textures[id].width).to.equal(spritesheet.data.frames[id].frame.w / spritesheet.resolution); expect(textures[id].height).to.equal(spritesheet.data.frames[id].frame.h / spritesheet.resolution); - expect(textures[id].textureCacheId).to.equal(id); + expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; expect(spritesheet.baseTexture).to.be.null; diff --git a/test/core/Texture.js b/test/core/Texture.js index 241ec3e..995ed40 100644 --- a/test/core/Texture.js +++ b/test/core/Texture.js @@ -1,11 +1,26 @@ 'use strict'; +const URL = 'foo.png'; +const NAME = 'foo'; +const NAME2 = 'bar'; + +function cleanCache() +{ + delete PIXI.utils.BaseTextureCache[URL]; + delete PIXI.utils.BaseTextureCache[NAME]; + delete PIXI.utils.BaseTextureCache[NAME2]; + + delete PIXI.utils.TextureCache[URL]; + delete PIXI.utils.TextureCache[NAME]; + delete PIXI.utils.TextureCache[NAME2]; +} + describe('PIXI.Texture', function () { it('should register Texture from Loader', function () { - const URL = 'foo.png'; - const NAME = 'bar'; + cleanCache(); + const image = new Image(); const texture = PIXI.Texture.fromLoader(image, URL, NAME); @@ -19,17 +34,97 @@ it('should remove Texture from cache on destroy', function () { - const NAME = 'foo'; - const NAME2 = 'bar'; + cleanCache(); + const texture = new PIXI.Texture(new PIXI.BaseTexture()); - PIXI.Texture.addTextureToCache(texture, NAME); - PIXI.Texture.addTextureToCache(texture, NAME2); - expect(texture.textureCacheId).to.equal(NAME); + PIXI.Texture.addToCache(texture, NAME); + PIXI.Texture.addToCache(texture, NAME2); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1); expect(PIXI.utils.TextureCache[NAME]).to.equal(texture); expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture); texture.destroy(); + expect(texture.textureCacheIds).to.equal(null); expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined); expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined); }); + + it('should be added to the texture cache correctly, ' + + 'and should remove only itself, not effecting the base texture and its cache', function () + { + cleanCache(); + + const texture = new PIXI.Texture(new PIXI.BaseTexture()); + + PIXI.BaseTexture.addToCache(texture.baseTexture, NAME); + PIXI.Texture.addToCache(texture, NAME); + expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture); + expect(PIXI.utils.TextureCache[NAME]).to.equal(texture); + PIXI.Texture.removeFromCache(NAME); + expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture); + expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined); + }); + + it('should be added to the texture cache correctly using legacy addTextureToCache, ' + + 'and should remove also remove the base texture from its cache with removeTextureFromCache', function () + { + cleanCache(); + + const texture = new PIXI.Texture(new PIXI.BaseTexture()); + + PIXI.BaseTexture.addToCache(texture.baseTexture, NAME); + PIXI.Texture.addTextureToCache(texture, NAME); + expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture); + expect(PIXI.utils.TextureCache[NAME]).to.equal(texture); + PIXI.Texture.removeTextureFromCache(NAME); + expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined); + }); + + it('should remove Texture from entire cache using removeFromCache (by Texture instance)', function () + { + cleanCache(); + + const texture = new PIXI.Texture(new PIXI.BaseTexture()); + + PIXI.Texture.addToCache(texture, NAME); + PIXI.Texture.addToCache(texture, NAME2); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1); + expect(PIXI.utils.TextureCache[NAME]).to.equal(texture); + expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture); + PIXI.Texture.removeFromCache(texture); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(-1); + expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.TextureCache[NAME2]).to.equal(undefined); + }); + + it('should remove Texture from single cache entry using removeFromCache (by id)', function () + { + cleanCache(); + + const texture = new PIXI.Texture(new PIXI.BaseTexture()); + + PIXI.Texture.addToCache(texture, NAME); + PIXI.Texture.addToCache(texture, NAME2); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0); + expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(1); + expect(PIXI.utils.TextureCache[NAME]).to.equal(texture); + expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture); + PIXI.Texture.removeFromCache(NAME); + expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1); + expect(texture.textureCacheIds.indexOf(NAME2)).to.equal(0); + expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined); + expect(PIXI.utils.TextureCache[NAME2]).to.equal(texture); + }); }); diff --git a/test/core/index.js b/test/core/index.js index 58dc30e..28636ab 100755 --- a/test/core/index.js +++ b/test/core/index.js @@ -27,6 +27,7 @@ require('./SpriteRenderer'); require('./WebGLRenderer'); require('./Ellipse'); +require('./BaseTexture'); require('./Texture'); require('./Ticker'); require('./filters');