diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 2b30b1c..7570332 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -12,7 +12,11 @@ const imageResourceName = `${resource.name}_image`; // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists - if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) + if (!resource.data + || resource.type !== Resource.TYPE.JSON + || !resource.data.frames + || this.resources[imageResourceName] + ) { next(); @@ -23,6 +27,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // Prepend url path unless the resource image is a data url diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 2b30b1c..7570332 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -12,7 +12,11 @@ const imageResourceName = `${resource.name}_image`; // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists - if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) + if (!resource.data + || resource.type !== Resource.TYPE.JSON + || !resource.data.frames + || this.resources[imageResourceName] + ) { next(); @@ -23,6 +27,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // Prepend url path unless the resource image is a data url diff --git a/src/loaders/textureParser.js b/src/loaders/textureParser.js index 6bcbee6..5398a7f 100644 --- a/src/loaders/textureParser.js +++ b/src/loaders/textureParser.js @@ -1,11 +1,12 @@ import * as core from '../core'; +import { Resource } from 'resource-loader'; export default function () { return function textureParser(resource, next) { // create a new texture if the data is an Image object - if (resource.data && resource.isImage) + if (resource.data && resource.type === Resource.TYPE.IMAGE) { const baseTexture = new core.BaseTexture(resource.data, null, core.utils.getResolutionOfUrl(resource.url)); diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 2b30b1c..7570332 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -12,7 +12,11 @@ const imageResourceName = `${resource.name}_image`; // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists - if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) + if (!resource.data + || resource.type !== Resource.TYPE.JSON + || !resource.data.frames + || this.resources[imageResourceName] + ) { next(); @@ -23,6 +27,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // Prepend url path unless the resource image is a data url diff --git a/src/loaders/textureParser.js b/src/loaders/textureParser.js index 6bcbee6..5398a7f 100644 --- a/src/loaders/textureParser.js +++ b/src/loaders/textureParser.js @@ -1,11 +1,12 @@ import * as core from '../core'; +import { Resource } from 'resource-loader'; export default function () { return function textureParser(resource, next) { // create a new texture if the data is an Image object - if (resource.data && resource.isImage) + if (resource.data && resource.type === Resource.TYPE.IMAGE) { const baseTexture = new core.BaseTexture(resource.data, null, core.utils.getResolutionOfUrl(resource.url)); diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index bbb0938..2e7addc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -35,7 +35,8 @@ * * @member {Float32Array} */ - this.uvs = uvs || new Float32Array([0, 0, + this.uvs = uvs || new Float32Array([ + 0, 0, 1, 0, 1, 1, 0, 1]); @@ -45,7 +46,8 @@ * * @member {Float32Array} */ - this.vertices = vertices || new Float32Array([0, 0, + this.vertices = vertices || new Float32Array([ + 0, 0, 100, 0, 100, 100, 0, 100]); diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 2b30b1c..7570332 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -12,7 +12,11 @@ const imageResourceName = `${resource.name}_image`; // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists - if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) + if (!resource.data + || resource.type !== Resource.TYPE.JSON + || !resource.data.frames + || this.resources[imageResourceName] + ) { next(); @@ -23,6 +27,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // Prepend url path unless the resource image is a data url diff --git a/src/loaders/textureParser.js b/src/loaders/textureParser.js index 6bcbee6..5398a7f 100644 --- a/src/loaders/textureParser.js +++ b/src/loaders/textureParser.js @@ -1,11 +1,12 @@ import * as core from '../core'; +import { Resource } from 'resource-loader'; export default function () { return function textureParser(resource, next) { // create a new texture if the data is an Image object - if (resource.data && resource.isImage) + if (resource.data && resource.type === Resource.TYPE.IMAGE) { const baseTexture = new core.BaseTexture(resource.data, null, core.utils.getResolutionOfUrl(resource.url)); diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index bbb0938..2e7addc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -35,7 +35,8 @@ * * @member {Float32Array} */ - this.uvs = uvs || new Float32Array([0, 0, + this.uvs = uvs || new Float32Array([ + 0, 0, 1, 0, 1, 1, 0, 1]); @@ -45,7 +46,8 @@ * * @member {Float32Array} */ - this.vertices = vertices || new Float32Array([0, 0, + this.vertices = vertices || new Float32Array([ + 0, 0, 100, 0, 100, 100, 0, 100]); diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index e6a0ed5..42048a8 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -237,7 +237,7 @@ continue; } - const frame = child.texture.frame; + const frame = child._texture.frame; context.globalAlpha = this.worldAlpha * child.alpha; @@ -305,10 +305,10 @@ finalHeight = frame.height; } - const resolution = child.texture.baseTexture.resolution; + const resolution = child._texture.baseTexture.resolution; context.drawImage( - child.texture.baseTexture.source, + child._texture.baseTexture.source, frame.x * resolution, frame.y * resolution, frame.width * resolution, diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 2b30b1c..7570332 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -12,7 +12,11 @@ const imageResourceName = `${resource.name}_image`; // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists - if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) + if (!resource.data + || resource.type !== Resource.TYPE.JSON + || !resource.data.frames + || this.resources[imageResourceName] + ) { next(); @@ -23,6 +27,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // Prepend url path unless the resource image is a data url diff --git a/src/loaders/textureParser.js b/src/loaders/textureParser.js index 6bcbee6..5398a7f 100644 --- a/src/loaders/textureParser.js +++ b/src/loaders/textureParser.js @@ -1,11 +1,12 @@ import * as core from '../core'; +import { Resource } from 'resource-loader'; export default function () { return function textureParser(resource, next) { // create a new texture if the data is an Image object - if (resource.data && resource.isImage) + if (resource.data && resource.type === Resource.TYPE.IMAGE) { const baseTexture = new core.BaseTexture(resource.data, null, core.utils.getResolutionOfUrl(resource.url)); diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index bbb0938..2e7addc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -35,7 +35,8 @@ * * @member {Float32Array} */ - this.uvs = uvs || new Float32Array([0, 0, + this.uvs = uvs || new Float32Array([ + 0, 0, 1, 0, 1, 1, 0, 1]); @@ -45,7 +46,8 @@ * * @member {Float32Array} */ - this.vertices = vertices || new Float32Array([0, 0, + this.vertices = vertices || new Float32Array([ + 0, 0, 100, 0, 100, 100, 0, 100]); diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index e6a0ed5..42048a8 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -237,7 +237,7 @@ continue; } - const frame = child.texture.frame; + const frame = child._texture.frame; context.globalAlpha = this.worldAlpha * child.alpha; @@ -305,10 +305,10 @@ finalHeight = frame.height; } - const resolution = child.texture.baseTexture.resolution; + const resolution = child._texture.baseTexture.resolution; context.drawImage( - child.texture.baseTexture.source, + child._texture.baseTexture.source, frame.x * resolution, frame.y * resolution, frame.width * resolution, diff --git a/test/core/Graphics.js b/test/core/Graphics.js index 0622cfc..50f775b 100644 --- a/test/core/Graphics.js +++ b/test/core/Graphics.js @@ -191,4 +191,21 @@ expect(graphics.currentPath).to.be.null; }); }); + + describe('_calculateBounds', () => + { + it('should only call updateLocalBounds once', () => + { + const graphics = new PIXI.Graphics(); + const spy = sinon.spy(graphics, 'updateLocalBounds'); + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + }); + }); }); diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 2b30b1c..7570332 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -12,7 +12,11 @@ const imageResourceName = `${resource.name}_image`; // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists - if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) + if (!resource.data + || resource.type !== Resource.TYPE.JSON + || !resource.data.frames + || this.resources[imageResourceName] + ) { next(); @@ -23,6 +27,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // Prepend url path unless the resource image is a data url diff --git a/src/loaders/textureParser.js b/src/loaders/textureParser.js index 6bcbee6..5398a7f 100644 --- a/src/loaders/textureParser.js +++ b/src/loaders/textureParser.js @@ -1,11 +1,12 @@ import * as core from '../core'; +import { Resource } from 'resource-loader'; export default function () { return function textureParser(resource, next) { // create a new texture if the data is an Image object - if (resource.data && resource.isImage) + if (resource.data && resource.type === Resource.TYPE.IMAGE) { const baseTexture = new core.BaseTexture(resource.data, null, core.utils.getResolutionOfUrl(resource.url)); diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index bbb0938..2e7addc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -35,7 +35,8 @@ * * @member {Float32Array} */ - this.uvs = uvs || new Float32Array([0, 0, + this.uvs = uvs || new Float32Array([ + 0, 0, 1, 0, 1, 1, 0, 1]); @@ -45,7 +46,8 @@ * * @member {Float32Array} */ - this.vertices = vertices || new Float32Array([0, 0, + this.vertices = vertices || new Float32Array([ + 0, 0, 100, 0, 100, 100, 0, 100]); diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index e6a0ed5..42048a8 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -237,7 +237,7 @@ continue; } - const frame = child.texture.frame; + const frame = child._texture.frame; context.globalAlpha = this.worldAlpha * child.alpha; @@ -305,10 +305,10 @@ finalHeight = frame.height; } - const resolution = child.texture.baseTexture.resolution; + const resolution = child._texture.baseTexture.resolution; context.drawImage( - child.texture.baseTexture.source, + child._texture.baseTexture.source, frame.x * resolution, frame.y * resolution, frame.width * resolution, diff --git a/test/core/Graphics.js b/test/core/Graphics.js index 0622cfc..50f775b 100644 --- a/test/core/Graphics.js +++ b/test/core/Graphics.js @@ -191,4 +191,21 @@ expect(graphics.currentPath).to.be.null; }); }); + + describe('_calculateBounds', () => + { + it('should only call updateLocalBounds once', () => + { + const graphics = new PIXI.Graphics(); + const spy = sinon.spy(graphics, 'updateLocalBounds'); + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + }); + }); }); diff --git a/test/core/Text.js b/test/core/Text.js index 4f67752..b8ce561 100644 --- a/test/core/Text.js +++ b/test/core/Text.js @@ -2,6 +2,40 @@ describe('PIXI.Text', function () { + describe('getFontStyle', function () + { + it('should be a valid API', function () + { + expect(PIXI.Text.getFontStyle).to.be.a.function; + }); + + it('should assume pixel fonts', function () + { + const style = PIXI.Text.getFontStyle({ fontSize: 72 }); + + expect(style).to.be.a.string; + expect(style).to.have.string(' 72px '); + }); + + it('should handle multiple fonts as array', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: ['Georgia', 'Arial', 'sans-serif'], + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + + it('should handle multiple fonts as string', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: 'Georgia, "Arial", sans-serif', + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + }); + describe('destroy', function () { it('should call through to Sprite.destroy', function () diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 2b30b1c..7570332 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -12,7 +12,11 @@ const imageResourceName = `${resource.name}_image`; // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists - if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) + if (!resource.data + || resource.type !== Resource.TYPE.JSON + || !resource.data.frames + || this.resources[imageResourceName] + ) { next(); @@ -23,6 +27,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // Prepend url path unless the resource image is a data url diff --git a/src/loaders/textureParser.js b/src/loaders/textureParser.js index 6bcbee6..5398a7f 100644 --- a/src/loaders/textureParser.js +++ b/src/loaders/textureParser.js @@ -1,11 +1,12 @@ import * as core from '../core'; +import { Resource } from 'resource-loader'; export default function () { return function textureParser(resource, next) { // create a new texture if the data is an Image object - if (resource.data && resource.isImage) + if (resource.data && resource.type === Resource.TYPE.IMAGE) { const baseTexture = new core.BaseTexture(resource.data, null, core.utils.getResolutionOfUrl(resource.url)); diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index bbb0938..2e7addc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -35,7 +35,8 @@ * * @member {Float32Array} */ - this.uvs = uvs || new Float32Array([0, 0, + this.uvs = uvs || new Float32Array([ + 0, 0, 1, 0, 1, 1, 0, 1]); @@ -45,7 +46,8 @@ * * @member {Float32Array} */ - this.vertices = vertices || new Float32Array([0, 0, + this.vertices = vertices || new Float32Array([ + 0, 0, 100, 0, 100, 100, 0, 100]); diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index e6a0ed5..42048a8 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -237,7 +237,7 @@ continue; } - const frame = child.texture.frame; + const frame = child._texture.frame; context.globalAlpha = this.worldAlpha * child.alpha; @@ -305,10 +305,10 @@ finalHeight = frame.height; } - const resolution = child.texture.baseTexture.resolution; + const resolution = child._texture.baseTexture.resolution; context.drawImage( - child.texture.baseTexture.source, + child._texture.baseTexture.source, frame.x * resolution, frame.y * resolution, frame.width * resolution, diff --git a/test/core/Graphics.js b/test/core/Graphics.js index 0622cfc..50f775b 100644 --- a/test/core/Graphics.js +++ b/test/core/Graphics.js @@ -191,4 +191,21 @@ expect(graphics.currentPath).to.be.null; }); }); + + describe('_calculateBounds', () => + { + it('should only call updateLocalBounds once', () => + { + const graphics = new PIXI.Graphics(); + const spy = sinon.spy(graphics, 'updateLocalBounds'); + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + }); + }); }); diff --git a/test/core/Text.js b/test/core/Text.js index 4f67752..b8ce561 100644 --- a/test/core/Text.js +++ b/test/core/Text.js @@ -2,6 +2,40 @@ describe('PIXI.Text', function () { + describe('getFontStyle', function () + { + it('should be a valid API', function () + { + expect(PIXI.Text.getFontStyle).to.be.a.function; + }); + + it('should assume pixel fonts', function () + { + const style = PIXI.Text.getFontStyle({ fontSize: 72 }); + + expect(style).to.be.a.string; + expect(style).to.have.string(' 72px '); + }); + + it('should handle multiple fonts as array', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: ['Georgia', 'Arial', 'sans-serif'], + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + + it('should handle multiple fonts as string', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: 'Georgia, "Arial", sans-serif', + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + }); + describe('destroy', function () { it('should call through to Sprite.destroy', function () diff --git a/test/index.js b/test/index.js index 50a85da..9689aa0 100755 --- a/test/index.js +++ b/test/index.js @@ -13,6 +13,7 @@ }); require('./core'); require('./interaction'); + require('./loaders'); require('./renders'); require('./prepare'); }); diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 2b30b1c..7570332 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -12,7 +12,11 @@ const imageResourceName = `${resource.name}_image`; // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists - if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) + if (!resource.data + || resource.type !== Resource.TYPE.JSON + || !resource.data.frames + || this.resources[imageResourceName] + ) { next(); @@ -23,6 +27,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // Prepend url path unless the resource image is a data url diff --git a/src/loaders/textureParser.js b/src/loaders/textureParser.js index 6bcbee6..5398a7f 100644 --- a/src/loaders/textureParser.js +++ b/src/loaders/textureParser.js @@ -1,11 +1,12 @@ import * as core from '../core'; +import { Resource } from 'resource-loader'; export default function () { return function textureParser(resource, next) { // create a new texture if the data is an Image object - if (resource.data && resource.isImage) + if (resource.data && resource.type === Resource.TYPE.IMAGE) { const baseTexture = new core.BaseTexture(resource.data, null, core.utils.getResolutionOfUrl(resource.url)); diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index bbb0938..2e7addc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -35,7 +35,8 @@ * * @member {Float32Array} */ - this.uvs = uvs || new Float32Array([0, 0, + this.uvs = uvs || new Float32Array([ + 0, 0, 1, 0, 1, 1, 0, 1]); @@ -45,7 +46,8 @@ * * @member {Float32Array} */ - this.vertices = vertices || new Float32Array([0, 0, + this.vertices = vertices || new Float32Array([ + 0, 0, 100, 0, 100, 100, 0, 100]); diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index e6a0ed5..42048a8 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -237,7 +237,7 @@ continue; } - const frame = child.texture.frame; + const frame = child._texture.frame; context.globalAlpha = this.worldAlpha * child.alpha; @@ -305,10 +305,10 @@ finalHeight = frame.height; } - const resolution = child.texture.baseTexture.resolution; + const resolution = child._texture.baseTexture.resolution; context.drawImage( - child.texture.baseTexture.source, + child._texture.baseTexture.source, frame.x * resolution, frame.y * resolution, frame.width * resolution, diff --git a/test/core/Graphics.js b/test/core/Graphics.js index 0622cfc..50f775b 100644 --- a/test/core/Graphics.js +++ b/test/core/Graphics.js @@ -191,4 +191,21 @@ expect(graphics.currentPath).to.be.null; }); }); + + describe('_calculateBounds', () => + { + it('should only call updateLocalBounds once', () => + { + const graphics = new PIXI.Graphics(); + const spy = sinon.spy(graphics, 'updateLocalBounds'); + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + }); + }); }); diff --git a/test/core/Text.js b/test/core/Text.js index 4f67752..b8ce561 100644 --- a/test/core/Text.js +++ b/test/core/Text.js @@ -2,6 +2,40 @@ describe('PIXI.Text', function () { + describe('getFontStyle', function () + { + it('should be a valid API', function () + { + expect(PIXI.Text.getFontStyle).to.be.a.function; + }); + + it('should assume pixel fonts', function () + { + const style = PIXI.Text.getFontStyle({ fontSize: 72 }); + + expect(style).to.be.a.string; + expect(style).to.have.string(' 72px '); + }); + + it('should handle multiple fonts as array', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: ['Georgia', 'Arial', 'sans-serif'], + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + + it('should handle multiple fonts as string', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: 'Georgia, "Arial", sans-serif', + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + }); + describe('destroy', function () { it('should call through to Sprite.destroy', function () diff --git a/test/index.js b/test/index.js index 50a85da..9689aa0 100755 --- a/test/index.js +++ b/test/index.js @@ -13,6 +13,7 @@ }); require('./core'); require('./interaction'); + require('./loaders'); require('./renders'); require('./prepare'); }); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js new file mode 100644 index 0000000..47d6819 --- /dev/null +++ b/test/interaction/InteractionManager.js @@ -0,0 +1,84 @@ +'use strict'; + +const MockPointer = require('./MockPointer'); + +describe('PIXI.interaction.InteractionManager', function () +{ + describe('onClick', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); + + describe('onTap', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); +}); diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 2b30b1c..7570332 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -12,7 +12,11 @@ const imageResourceName = `${resource.name}_image`; // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists - if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) + if (!resource.data + || resource.type !== Resource.TYPE.JSON + || !resource.data.frames + || this.resources[imageResourceName] + ) { next(); @@ -23,6 +27,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // Prepend url path unless the resource image is a data url diff --git a/src/loaders/textureParser.js b/src/loaders/textureParser.js index 6bcbee6..5398a7f 100644 --- a/src/loaders/textureParser.js +++ b/src/loaders/textureParser.js @@ -1,11 +1,12 @@ import * as core from '../core'; +import { Resource } from 'resource-loader'; export default function () { return function textureParser(resource, next) { // create a new texture if the data is an Image object - if (resource.data && resource.isImage) + if (resource.data && resource.type === Resource.TYPE.IMAGE) { const baseTexture = new core.BaseTexture(resource.data, null, core.utils.getResolutionOfUrl(resource.url)); diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index bbb0938..2e7addc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -35,7 +35,8 @@ * * @member {Float32Array} */ - this.uvs = uvs || new Float32Array([0, 0, + this.uvs = uvs || new Float32Array([ + 0, 0, 1, 0, 1, 1, 0, 1]); @@ -45,7 +46,8 @@ * * @member {Float32Array} */ - this.vertices = vertices || new Float32Array([0, 0, + this.vertices = vertices || new Float32Array([ + 0, 0, 100, 0, 100, 100, 0, 100]); diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index e6a0ed5..42048a8 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -237,7 +237,7 @@ continue; } - const frame = child.texture.frame; + const frame = child._texture.frame; context.globalAlpha = this.worldAlpha * child.alpha; @@ -305,10 +305,10 @@ finalHeight = frame.height; } - const resolution = child.texture.baseTexture.resolution; + const resolution = child._texture.baseTexture.resolution; context.drawImage( - child.texture.baseTexture.source, + child._texture.baseTexture.source, frame.x * resolution, frame.y * resolution, frame.width * resolution, diff --git a/test/core/Graphics.js b/test/core/Graphics.js index 0622cfc..50f775b 100644 --- a/test/core/Graphics.js +++ b/test/core/Graphics.js @@ -191,4 +191,21 @@ expect(graphics.currentPath).to.be.null; }); }); + + describe('_calculateBounds', () => + { + it('should only call updateLocalBounds once', () => + { + const graphics = new PIXI.Graphics(); + const spy = sinon.spy(graphics, 'updateLocalBounds'); + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + }); + }); }); diff --git a/test/core/Text.js b/test/core/Text.js index 4f67752..b8ce561 100644 --- a/test/core/Text.js +++ b/test/core/Text.js @@ -2,6 +2,40 @@ describe('PIXI.Text', function () { + describe('getFontStyle', function () + { + it('should be a valid API', function () + { + expect(PIXI.Text.getFontStyle).to.be.a.function; + }); + + it('should assume pixel fonts', function () + { + const style = PIXI.Text.getFontStyle({ fontSize: 72 }); + + expect(style).to.be.a.string; + expect(style).to.have.string(' 72px '); + }); + + it('should handle multiple fonts as array', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: ['Georgia', 'Arial', 'sans-serif'], + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + + it('should handle multiple fonts as string', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: 'Georgia, "Arial", sans-serif', + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + }); + describe('destroy', function () { it('should call through to Sprite.destroy', function () diff --git a/test/index.js b/test/index.js index 50a85da..9689aa0 100755 --- a/test/index.js +++ b/test/index.js @@ -13,6 +13,7 @@ }); require('./core'); require('./interaction'); + require('./loaders'); require('./renders'); require('./prepare'); }); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js new file mode 100644 index 0000000..47d6819 --- /dev/null +++ b/test/interaction/InteractionManager.js @@ -0,0 +1,84 @@ +'use strict'; + +const MockPointer = require('./MockPointer'); + +describe('PIXI.interaction.InteractionManager', function () +{ + describe('onClick', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); + + describe('onTap', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); +}); diff --git a/test/interaction/MockPointer.js b/test/interaction/MockPointer.js new file mode 100644 index 0000000..5bac0d3 --- /dev/null +++ b/test/interaction/MockPointer.js @@ -0,0 +1,115 @@ +'use strict'; + +/** + * Use this to mock mouse/touch/pointer events + * + * @class + */ +class MockPointer { + /** + * @param {PIXI.Container} stage - The root of the scene tree + * @param {number} [width=100] - Width of the renderer + * @param {number} [height=100] - Height of the renderer + */ + constructor(stage, width, height) + { + this.stage = stage; + this.renderer = new PIXI.CanvasRenderer(width || 100, height || 100); + this.renderer.sayHello = () => { /* empty */ }; + this.interaction = this.renderer.plugins.interaction; + } + + /** + * @private + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + setPosition(x, y) + { + this.renderer.plugins.interaction.mapPositionToPoint = (point) => + { + point.x = x; + point.y = y; + }; + } + + /** + * @private + */ + render() + { + this.renderer.render(this.stage); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + click(x, y) + { + this.mousedown(x, y); + this.mouseup(x, y); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + mousedown(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onMouseDown({ clientX: 0, clientY: 0, preventDefault: sinon.stub() }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + mouseup(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onMouseUp({ clientX: 0, clientY: 0, preventDefault: sinon.stub() }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + tap(x, y) + { + this.touchstart(x, y); + this.touchend(x, y); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + touchstart(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onTouchStart({ + preventDefault: sinon.stub(), + changedTouches: [new Touch({ identifier: 0, target: this.renderer.view })], + }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + touchend(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onTouchEnd({ + preventDefault: sinon.stub(), + changedTouches: [new Touch({ identifier: 0, target: this.renderer.view })], + }); + } +} + +module.exports = MockPointer; diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 2b30b1c..7570332 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -12,7 +12,11 @@ const imageResourceName = `${resource.name}_image`; // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists - if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) + if (!resource.data + || resource.type !== Resource.TYPE.JSON + || !resource.data.frames + || this.resources[imageResourceName] + ) { next(); @@ -23,6 +27,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // Prepend url path unless the resource image is a data url diff --git a/src/loaders/textureParser.js b/src/loaders/textureParser.js index 6bcbee6..5398a7f 100644 --- a/src/loaders/textureParser.js +++ b/src/loaders/textureParser.js @@ -1,11 +1,12 @@ import * as core from '../core'; +import { Resource } from 'resource-loader'; export default function () { return function textureParser(resource, next) { // create a new texture if the data is an Image object - if (resource.data && resource.isImage) + if (resource.data && resource.type === Resource.TYPE.IMAGE) { const baseTexture = new core.BaseTexture(resource.data, null, core.utils.getResolutionOfUrl(resource.url)); diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index bbb0938..2e7addc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -35,7 +35,8 @@ * * @member {Float32Array} */ - this.uvs = uvs || new Float32Array([0, 0, + this.uvs = uvs || new Float32Array([ + 0, 0, 1, 0, 1, 1, 0, 1]); @@ -45,7 +46,8 @@ * * @member {Float32Array} */ - this.vertices = vertices || new Float32Array([0, 0, + this.vertices = vertices || new Float32Array([ + 0, 0, 100, 0, 100, 100, 0, 100]); diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index e6a0ed5..42048a8 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -237,7 +237,7 @@ continue; } - const frame = child.texture.frame; + const frame = child._texture.frame; context.globalAlpha = this.worldAlpha * child.alpha; @@ -305,10 +305,10 @@ finalHeight = frame.height; } - const resolution = child.texture.baseTexture.resolution; + const resolution = child._texture.baseTexture.resolution; context.drawImage( - child.texture.baseTexture.source, + child._texture.baseTexture.source, frame.x * resolution, frame.y * resolution, frame.width * resolution, diff --git a/test/core/Graphics.js b/test/core/Graphics.js index 0622cfc..50f775b 100644 --- a/test/core/Graphics.js +++ b/test/core/Graphics.js @@ -191,4 +191,21 @@ expect(graphics.currentPath).to.be.null; }); }); + + describe('_calculateBounds', () => + { + it('should only call updateLocalBounds once', () => + { + const graphics = new PIXI.Graphics(); + const spy = sinon.spy(graphics, 'updateLocalBounds'); + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + }); + }); }); diff --git a/test/core/Text.js b/test/core/Text.js index 4f67752..b8ce561 100644 --- a/test/core/Text.js +++ b/test/core/Text.js @@ -2,6 +2,40 @@ describe('PIXI.Text', function () { + describe('getFontStyle', function () + { + it('should be a valid API', function () + { + expect(PIXI.Text.getFontStyle).to.be.a.function; + }); + + it('should assume pixel fonts', function () + { + const style = PIXI.Text.getFontStyle({ fontSize: 72 }); + + expect(style).to.be.a.string; + expect(style).to.have.string(' 72px '); + }); + + it('should handle multiple fonts as array', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: ['Georgia', 'Arial', 'sans-serif'], + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + + it('should handle multiple fonts as string', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: 'Georgia, "Arial", sans-serif', + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + }); + describe('destroy', function () { it('should call through to Sprite.destroy', function () diff --git a/test/index.js b/test/index.js index 50a85da..9689aa0 100755 --- a/test/index.js +++ b/test/index.js @@ -13,6 +13,7 @@ }); require('./core'); require('./interaction'); + require('./loaders'); require('./renders'); require('./prepare'); }); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js new file mode 100644 index 0000000..47d6819 --- /dev/null +++ b/test/interaction/InteractionManager.js @@ -0,0 +1,84 @@ +'use strict'; + +const MockPointer = require('./MockPointer'); + +describe('PIXI.interaction.InteractionManager', function () +{ + describe('onClick', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); + + describe('onTap', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); +}); diff --git a/test/interaction/MockPointer.js b/test/interaction/MockPointer.js new file mode 100644 index 0000000..5bac0d3 --- /dev/null +++ b/test/interaction/MockPointer.js @@ -0,0 +1,115 @@ +'use strict'; + +/** + * Use this to mock mouse/touch/pointer events + * + * @class + */ +class MockPointer { + /** + * @param {PIXI.Container} stage - The root of the scene tree + * @param {number} [width=100] - Width of the renderer + * @param {number} [height=100] - Height of the renderer + */ + constructor(stage, width, height) + { + this.stage = stage; + this.renderer = new PIXI.CanvasRenderer(width || 100, height || 100); + this.renderer.sayHello = () => { /* empty */ }; + this.interaction = this.renderer.plugins.interaction; + } + + /** + * @private + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + setPosition(x, y) + { + this.renderer.plugins.interaction.mapPositionToPoint = (point) => + { + point.x = x; + point.y = y; + }; + } + + /** + * @private + */ + render() + { + this.renderer.render(this.stage); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + click(x, y) + { + this.mousedown(x, y); + this.mouseup(x, y); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + mousedown(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onMouseDown({ clientX: 0, clientY: 0, preventDefault: sinon.stub() }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + mouseup(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onMouseUp({ clientX: 0, clientY: 0, preventDefault: sinon.stub() }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + tap(x, y) + { + this.touchstart(x, y); + this.touchend(x, y); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + touchstart(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onTouchStart({ + preventDefault: sinon.stub(), + changedTouches: [new Touch({ identifier: 0, target: this.renderer.view })], + }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + touchend(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onTouchEnd({ + preventDefault: sinon.stub(), + changedTouches: [new Touch({ identifier: 0, target: this.renderer.view })], + }); + } +} + +module.exports = MockPointer; diff --git a/test/interaction/index.js b/test/interaction/index.js index 9c88370..fd84330 100644 --- a/test/interaction/index.js +++ b/test/interaction/index.js @@ -1,3 +1,4 @@ 'use strict'; require('./InteractionData'); +require('./InteractionManager'); diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 2b30b1c..7570332 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -12,7 +12,11 @@ const imageResourceName = `${resource.name}_image`; // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists - if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) + if (!resource.data + || resource.type !== Resource.TYPE.JSON + || !resource.data.frames + || this.resources[imageResourceName] + ) { next(); @@ -23,6 +27,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // Prepend url path unless the resource image is a data url diff --git a/src/loaders/textureParser.js b/src/loaders/textureParser.js index 6bcbee6..5398a7f 100644 --- a/src/loaders/textureParser.js +++ b/src/loaders/textureParser.js @@ -1,11 +1,12 @@ import * as core from '../core'; +import { Resource } from 'resource-loader'; export default function () { return function textureParser(resource, next) { // create a new texture if the data is an Image object - if (resource.data && resource.isImage) + if (resource.data && resource.type === Resource.TYPE.IMAGE) { const baseTexture = new core.BaseTexture(resource.data, null, core.utils.getResolutionOfUrl(resource.url)); diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index bbb0938..2e7addc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -35,7 +35,8 @@ * * @member {Float32Array} */ - this.uvs = uvs || new Float32Array([0, 0, + this.uvs = uvs || new Float32Array([ + 0, 0, 1, 0, 1, 1, 0, 1]); @@ -45,7 +46,8 @@ * * @member {Float32Array} */ - this.vertices = vertices || new Float32Array([0, 0, + this.vertices = vertices || new Float32Array([ + 0, 0, 100, 0, 100, 100, 0, 100]); diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index e6a0ed5..42048a8 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -237,7 +237,7 @@ continue; } - const frame = child.texture.frame; + const frame = child._texture.frame; context.globalAlpha = this.worldAlpha * child.alpha; @@ -305,10 +305,10 @@ finalHeight = frame.height; } - const resolution = child.texture.baseTexture.resolution; + const resolution = child._texture.baseTexture.resolution; context.drawImage( - child.texture.baseTexture.source, + child._texture.baseTexture.source, frame.x * resolution, frame.y * resolution, frame.width * resolution, diff --git a/test/core/Graphics.js b/test/core/Graphics.js index 0622cfc..50f775b 100644 --- a/test/core/Graphics.js +++ b/test/core/Graphics.js @@ -191,4 +191,21 @@ expect(graphics.currentPath).to.be.null; }); }); + + describe('_calculateBounds', () => + { + it('should only call updateLocalBounds once', () => + { + const graphics = new PIXI.Graphics(); + const spy = sinon.spy(graphics, 'updateLocalBounds'); + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + }); + }); }); diff --git a/test/core/Text.js b/test/core/Text.js index 4f67752..b8ce561 100644 --- a/test/core/Text.js +++ b/test/core/Text.js @@ -2,6 +2,40 @@ describe('PIXI.Text', function () { + describe('getFontStyle', function () + { + it('should be a valid API', function () + { + expect(PIXI.Text.getFontStyle).to.be.a.function; + }); + + it('should assume pixel fonts', function () + { + const style = PIXI.Text.getFontStyle({ fontSize: 72 }); + + expect(style).to.be.a.string; + expect(style).to.have.string(' 72px '); + }); + + it('should handle multiple fonts as array', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: ['Georgia', 'Arial', 'sans-serif'], + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + + it('should handle multiple fonts as string', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: 'Georgia, "Arial", sans-serif', + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + }); + describe('destroy', function () { it('should call through to Sprite.destroy', function () diff --git a/test/index.js b/test/index.js index 50a85da..9689aa0 100755 --- a/test/index.js +++ b/test/index.js @@ -13,6 +13,7 @@ }); require('./core'); require('./interaction'); + require('./loaders'); require('./renders'); require('./prepare'); }); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js new file mode 100644 index 0000000..47d6819 --- /dev/null +++ b/test/interaction/InteractionManager.js @@ -0,0 +1,84 @@ +'use strict'; + +const MockPointer = require('./MockPointer'); + +describe('PIXI.interaction.InteractionManager', function () +{ + describe('onClick', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); + + describe('onTap', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); +}); diff --git a/test/interaction/MockPointer.js b/test/interaction/MockPointer.js new file mode 100644 index 0000000..5bac0d3 --- /dev/null +++ b/test/interaction/MockPointer.js @@ -0,0 +1,115 @@ +'use strict'; + +/** + * Use this to mock mouse/touch/pointer events + * + * @class + */ +class MockPointer { + /** + * @param {PIXI.Container} stage - The root of the scene tree + * @param {number} [width=100] - Width of the renderer + * @param {number} [height=100] - Height of the renderer + */ + constructor(stage, width, height) + { + this.stage = stage; + this.renderer = new PIXI.CanvasRenderer(width || 100, height || 100); + this.renderer.sayHello = () => { /* empty */ }; + this.interaction = this.renderer.plugins.interaction; + } + + /** + * @private + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + setPosition(x, y) + { + this.renderer.plugins.interaction.mapPositionToPoint = (point) => + { + point.x = x; + point.y = y; + }; + } + + /** + * @private + */ + render() + { + this.renderer.render(this.stage); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + click(x, y) + { + this.mousedown(x, y); + this.mouseup(x, y); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + mousedown(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onMouseDown({ clientX: 0, clientY: 0, preventDefault: sinon.stub() }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + mouseup(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onMouseUp({ clientX: 0, clientY: 0, preventDefault: sinon.stub() }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + tap(x, y) + { + this.touchstart(x, y); + this.touchend(x, y); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + touchstart(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onTouchStart({ + preventDefault: sinon.stub(), + changedTouches: [new Touch({ identifier: 0, target: this.renderer.view })], + }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + touchend(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onTouchEnd({ + preventDefault: sinon.stub(), + changedTouches: [new Touch({ identifier: 0, target: this.renderer.view })], + }); + } +} + +module.exports = MockPointer; diff --git a/test/interaction/index.js b/test/interaction/index.js index 9c88370..fd84330 100644 --- a/test/interaction/index.js +++ b/test/interaction/index.js @@ -1,3 +1,4 @@ 'use strict'; require('./InteractionData'); +require('./InteractionManager'); diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js new file mode 100644 index 0000000..7c77343 --- /dev/null +++ b/test/loaders/bitmapFontParser.js @@ -0,0 +1,46 @@ +'use strict'; + +describe('PIXI.loaders.bitmapFontParser', function () +{ + it('should exist and return a function', function () + { + expect(PIXI.loaders.bitmapFontParser).to.be.a('function'); + expect(PIXI.loaders.bitmapFontParser()).to.be.a('function'); + }); + + it('should do nothing if the resource is not XML', function () + { + const spy = sinon.spy(); + const res = {}; + + PIXI.loaders.bitmapFontParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.textures).to.be.undefined; + }); + + it('should do nothing if the resource is not properly formatted XML', function () + { + const spy = sinon.spy(); + const res = { data: document.createDocumentFragment() }; + + PIXI.loaders.bitmapFontParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.textures).to.be.undefined; + }); + + // TODO: Test the texture cache code path. + // TODO: Test the loading texture code path. + // TODO: Test data-url code paths. +}); + +describe('PIXI.loaders.parseBitmapFontData', function () +{ + it('should exist', function () + { + expect(PIXI.loaders.parseBitmapFontData).to.be.a('function'); + }); + + // TODO: Test the parser code. +}); diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 2b30b1c..7570332 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -12,7 +12,11 @@ const imageResourceName = `${resource.name}_image`; // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists - if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) + if (!resource.data + || resource.type !== Resource.TYPE.JSON + || !resource.data.frames + || this.resources[imageResourceName] + ) { next(); @@ -23,6 +27,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // Prepend url path unless the resource image is a data url diff --git a/src/loaders/textureParser.js b/src/loaders/textureParser.js index 6bcbee6..5398a7f 100644 --- a/src/loaders/textureParser.js +++ b/src/loaders/textureParser.js @@ -1,11 +1,12 @@ import * as core from '../core'; +import { Resource } from 'resource-loader'; export default function () { return function textureParser(resource, next) { // create a new texture if the data is an Image object - if (resource.data && resource.isImage) + if (resource.data && resource.type === Resource.TYPE.IMAGE) { const baseTexture = new core.BaseTexture(resource.data, null, core.utils.getResolutionOfUrl(resource.url)); diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index bbb0938..2e7addc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -35,7 +35,8 @@ * * @member {Float32Array} */ - this.uvs = uvs || new Float32Array([0, 0, + this.uvs = uvs || new Float32Array([ + 0, 0, 1, 0, 1, 1, 0, 1]); @@ -45,7 +46,8 @@ * * @member {Float32Array} */ - this.vertices = vertices || new Float32Array([0, 0, + this.vertices = vertices || new Float32Array([ + 0, 0, 100, 0, 100, 100, 0, 100]); diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index e6a0ed5..42048a8 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -237,7 +237,7 @@ continue; } - const frame = child.texture.frame; + const frame = child._texture.frame; context.globalAlpha = this.worldAlpha * child.alpha; @@ -305,10 +305,10 @@ finalHeight = frame.height; } - const resolution = child.texture.baseTexture.resolution; + const resolution = child._texture.baseTexture.resolution; context.drawImage( - child.texture.baseTexture.source, + child._texture.baseTexture.source, frame.x * resolution, frame.y * resolution, frame.width * resolution, diff --git a/test/core/Graphics.js b/test/core/Graphics.js index 0622cfc..50f775b 100644 --- a/test/core/Graphics.js +++ b/test/core/Graphics.js @@ -191,4 +191,21 @@ expect(graphics.currentPath).to.be.null; }); }); + + describe('_calculateBounds', () => + { + it('should only call updateLocalBounds once', () => + { + const graphics = new PIXI.Graphics(); + const spy = sinon.spy(graphics, 'updateLocalBounds'); + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + }); + }); }); diff --git a/test/core/Text.js b/test/core/Text.js index 4f67752..b8ce561 100644 --- a/test/core/Text.js +++ b/test/core/Text.js @@ -2,6 +2,40 @@ describe('PIXI.Text', function () { + describe('getFontStyle', function () + { + it('should be a valid API', function () + { + expect(PIXI.Text.getFontStyle).to.be.a.function; + }); + + it('should assume pixel fonts', function () + { + const style = PIXI.Text.getFontStyle({ fontSize: 72 }); + + expect(style).to.be.a.string; + expect(style).to.have.string(' 72px '); + }); + + it('should handle multiple fonts as array', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: ['Georgia', 'Arial', 'sans-serif'], + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + + it('should handle multiple fonts as string', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: 'Georgia, "Arial", sans-serif', + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + }); + describe('destroy', function () { it('should call through to Sprite.destroy', function () diff --git a/test/index.js b/test/index.js index 50a85da..9689aa0 100755 --- a/test/index.js +++ b/test/index.js @@ -13,6 +13,7 @@ }); require('./core'); require('./interaction'); + require('./loaders'); require('./renders'); require('./prepare'); }); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js new file mode 100644 index 0000000..47d6819 --- /dev/null +++ b/test/interaction/InteractionManager.js @@ -0,0 +1,84 @@ +'use strict'; + +const MockPointer = require('./MockPointer'); + +describe('PIXI.interaction.InteractionManager', function () +{ + describe('onClick', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); + + describe('onTap', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); +}); diff --git a/test/interaction/MockPointer.js b/test/interaction/MockPointer.js new file mode 100644 index 0000000..5bac0d3 --- /dev/null +++ b/test/interaction/MockPointer.js @@ -0,0 +1,115 @@ +'use strict'; + +/** + * Use this to mock mouse/touch/pointer events + * + * @class + */ +class MockPointer { + /** + * @param {PIXI.Container} stage - The root of the scene tree + * @param {number} [width=100] - Width of the renderer + * @param {number} [height=100] - Height of the renderer + */ + constructor(stage, width, height) + { + this.stage = stage; + this.renderer = new PIXI.CanvasRenderer(width || 100, height || 100); + this.renderer.sayHello = () => { /* empty */ }; + this.interaction = this.renderer.plugins.interaction; + } + + /** + * @private + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + setPosition(x, y) + { + this.renderer.plugins.interaction.mapPositionToPoint = (point) => + { + point.x = x; + point.y = y; + }; + } + + /** + * @private + */ + render() + { + this.renderer.render(this.stage); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + click(x, y) + { + this.mousedown(x, y); + this.mouseup(x, y); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + mousedown(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onMouseDown({ clientX: 0, clientY: 0, preventDefault: sinon.stub() }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + mouseup(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onMouseUp({ clientX: 0, clientY: 0, preventDefault: sinon.stub() }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + tap(x, y) + { + this.touchstart(x, y); + this.touchend(x, y); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + touchstart(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onTouchStart({ + preventDefault: sinon.stub(), + changedTouches: [new Touch({ identifier: 0, target: this.renderer.view })], + }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + touchend(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onTouchEnd({ + preventDefault: sinon.stub(), + changedTouches: [new Touch({ identifier: 0, target: this.renderer.view })], + }); + } +} + +module.exports = MockPointer; diff --git a/test/interaction/index.js b/test/interaction/index.js index 9c88370..fd84330 100644 --- a/test/interaction/index.js +++ b/test/interaction/index.js @@ -1,3 +1,4 @@ 'use strict'; require('./InteractionData'); +require('./InteractionManager'); diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js new file mode 100644 index 0000000..7c77343 --- /dev/null +++ b/test/loaders/bitmapFontParser.js @@ -0,0 +1,46 @@ +'use strict'; + +describe('PIXI.loaders.bitmapFontParser', function () +{ + it('should exist and return a function', function () + { + expect(PIXI.loaders.bitmapFontParser).to.be.a('function'); + expect(PIXI.loaders.bitmapFontParser()).to.be.a('function'); + }); + + it('should do nothing if the resource is not XML', function () + { + const spy = sinon.spy(); + const res = {}; + + PIXI.loaders.bitmapFontParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.textures).to.be.undefined; + }); + + it('should do nothing if the resource is not properly formatted XML', function () + { + const spy = sinon.spy(); + const res = { data: document.createDocumentFragment() }; + + PIXI.loaders.bitmapFontParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.textures).to.be.undefined; + }); + + // TODO: Test the texture cache code path. + // TODO: Test the loading texture code path. + // TODO: Test data-url code paths. +}); + +describe('PIXI.loaders.parseBitmapFontData', function () +{ + it('should exist', function () + { + expect(PIXI.loaders.parseBitmapFontData).to.be.a('function'); + }); + + // TODO: Test the parser code. +}); diff --git a/test/loaders/index.js b/test/loaders/index.js new file mode 100644 index 0000000..97fc849 --- /dev/null +++ b/test/loaders/index.js @@ -0,0 +1,6 @@ +'use strict'; + +require('./bitmapFontParser'); +require('./loader'); +require('./spritesheetParser'); +require('./textureParser'); diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 2b30b1c..7570332 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -12,7 +12,11 @@ const imageResourceName = `${resource.name}_image`; // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists - if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) + if (!resource.data + || resource.type !== Resource.TYPE.JSON + || !resource.data.frames + || this.resources[imageResourceName] + ) { next(); @@ -23,6 +27,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // Prepend url path unless the resource image is a data url diff --git a/src/loaders/textureParser.js b/src/loaders/textureParser.js index 6bcbee6..5398a7f 100644 --- a/src/loaders/textureParser.js +++ b/src/loaders/textureParser.js @@ -1,11 +1,12 @@ import * as core from '../core'; +import { Resource } from 'resource-loader'; export default function () { return function textureParser(resource, next) { // create a new texture if the data is an Image object - if (resource.data && resource.isImage) + if (resource.data && resource.type === Resource.TYPE.IMAGE) { const baseTexture = new core.BaseTexture(resource.data, null, core.utils.getResolutionOfUrl(resource.url)); diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index bbb0938..2e7addc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -35,7 +35,8 @@ * * @member {Float32Array} */ - this.uvs = uvs || new Float32Array([0, 0, + this.uvs = uvs || new Float32Array([ + 0, 0, 1, 0, 1, 1, 0, 1]); @@ -45,7 +46,8 @@ * * @member {Float32Array} */ - this.vertices = vertices || new Float32Array([0, 0, + this.vertices = vertices || new Float32Array([ + 0, 0, 100, 0, 100, 100, 0, 100]); diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index e6a0ed5..42048a8 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -237,7 +237,7 @@ continue; } - const frame = child.texture.frame; + const frame = child._texture.frame; context.globalAlpha = this.worldAlpha * child.alpha; @@ -305,10 +305,10 @@ finalHeight = frame.height; } - const resolution = child.texture.baseTexture.resolution; + const resolution = child._texture.baseTexture.resolution; context.drawImage( - child.texture.baseTexture.source, + child._texture.baseTexture.source, frame.x * resolution, frame.y * resolution, frame.width * resolution, diff --git a/test/core/Graphics.js b/test/core/Graphics.js index 0622cfc..50f775b 100644 --- a/test/core/Graphics.js +++ b/test/core/Graphics.js @@ -191,4 +191,21 @@ expect(graphics.currentPath).to.be.null; }); }); + + describe('_calculateBounds', () => + { + it('should only call updateLocalBounds once', () => + { + const graphics = new PIXI.Graphics(); + const spy = sinon.spy(graphics, 'updateLocalBounds'); + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + }); + }); }); diff --git a/test/core/Text.js b/test/core/Text.js index 4f67752..b8ce561 100644 --- a/test/core/Text.js +++ b/test/core/Text.js @@ -2,6 +2,40 @@ describe('PIXI.Text', function () { + describe('getFontStyle', function () + { + it('should be a valid API', function () + { + expect(PIXI.Text.getFontStyle).to.be.a.function; + }); + + it('should assume pixel fonts', function () + { + const style = PIXI.Text.getFontStyle({ fontSize: 72 }); + + expect(style).to.be.a.string; + expect(style).to.have.string(' 72px '); + }); + + it('should handle multiple fonts as array', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: ['Georgia', 'Arial', 'sans-serif'], + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + + it('should handle multiple fonts as string', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: 'Georgia, "Arial", sans-serif', + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + }); + describe('destroy', function () { it('should call through to Sprite.destroy', function () diff --git a/test/index.js b/test/index.js index 50a85da..9689aa0 100755 --- a/test/index.js +++ b/test/index.js @@ -13,6 +13,7 @@ }); require('./core'); require('./interaction'); + require('./loaders'); require('./renders'); require('./prepare'); }); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js new file mode 100644 index 0000000..47d6819 --- /dev/null +++ b/test/interaction/InteractionManager.js @@ -0,0 +1,84 @@ +'use strict'; + +const MockPointer = require('./MockPointer'); + +describe('PIXI.interaction.InteractionManager', function () +{ + describe('onClick', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); + + describe('onTap', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); +}); diff --git a/test/interaction/MockPointer.js b/test/interaction/MockPointer.js new file mode 100644 index 0000000..5bac0d3 --- /dev/null +++ b/test/interaction/MockPointer.js @@ -0,0 +1,115 @@ +'use strict'; + +/** + * Use this to mock mouse/touch/pointer events + * + * @class + */ +class MockPointer { + /** + * @param {PIXI.Container} stage - The root of the scene tree + * @param {number} [width=100] - Width of the renderer + * @param {number} [height=100] - Height of the renderer + */ + constructor(stage, width, height) + { + this.stage = stage; + this.renderer = new PIXI.CanvasRenderer(width || 100, height || 100); + this.renderer.sayHello = () => { /* empty */ }; + this.interaction = this.renderer.plugins.interaction; + } + + /** + * @private + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + setPosition(x, y) + { + this.renderer.plugins.interaction.mapPositionToPoint = (point) => + { + point.x = x; + point.y = y; + }; + } + + /** + * @private + */ + render() + { + this.renderer.render(this.stage); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + click(x, y) + { + this.mousedown(x, y); + this.mouseup(x, y); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + mousedown(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onMouseDown({ clientX: 0, clientY: 0, preventDefault: sinon.stub() }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + mouseup(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onMouseUp({ clientX: 0, clientY: 0, preventDefault: sinon.stub() }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + tap(x, y) + { + this.touchstart(x, y); + this.touchend(x, y); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + touchstart(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onTouchStart({ + preventDefault: sinon.stub(), + changedTouches: [new Touch({ identifier: 0, target: this.renderer.view })], + }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + touchend(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onTouchEnd({ + preventDefault: sinon.stub(), + changedTouches: [new Touch({ identifier: 0, target: this.renderer.view })], + }); + } +} + +module.exports = MockPointer; diff --git a/test/interaction/index.js b/test/interaction/index.js index 9c88370..fd84330 100644 --- a/test/interaction/index.js +++ b/test/interaction/index.js @@ -1,3 +1,4 @@ 'use strict'; require('./InteractionData'); +require('./InteractionManager'); diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js new file mode 100644 index 0000000..7c77343 --- /dev/null +++ b/test/loaders/bitmapFontParser.js @@ -0,0 +1,46 @@ +'use strict'; + +describe('PIXI.loaders.bitmapFontParser', function () +{ + it('should exist and return a function', function () + { + expect(PIXI.loaders.bitmapFontParser).to.be.a('function'); + expect(PIXI.loaders.bitmapFontParser()).to.be.a('function'); + }); + + it('should do nothing if the resource is not XML', function () + { + const spy = sinon.spy(); + const res = {}; + + PIXI.loaders.bitmapFontParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.textures).to.be.undefined; + }); + + it('should do nothing if the resource is not properly formatted XML', function () + { + const spy = sinon.spy(); + const res = { data: document.createDocumentFragment() }; + + PIXI.loaders.bitmapFontParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.textures).to.be.undefined; + }); + + // TODO: Test the texture cache code path. + // TODO: Test the loading texture code path. + // TODO: Test data-url code paths. +}); + +describe('PIXI.loaders.parseBitmapFontData', function () +{ + it('should exist', function () + { + expect(PIXI.loaders.parseBitmapFontData).to.be.a('function'); + }); + + // TODO: Test the parser code. +}); diff --git a/test/loaders/index.js b/test/loaders/index.js new file mode 100644 index 0000000..97fc849 --- /dev/null +++ b/test/loaders/index.js @@ -0,0 +1,6 @@ +'use strict'; + +require('./bitmapFontParser'); +require('./loader'); +require('./spritesheetParser'); +require('./textureParser'); diff --git a/test/loaders/loader.js b/test/loaders/loader.js new file mode 100644 index 0000000..8ec6793 --- /dev/null +++ b/test/loaders/loader.js @@ -0,0 +1,9 @@ +'use strict'; + +describe('PIXI.loaders.Loader', function () +{ + it('should exist', function () + { + expect(PIXI.loaders.Loader).to.be.a('function'); + }); +}); diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 2b30b1c..7570332 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -12,7 +12,11 @@ const imageResourceName = `${resource.name}_image`; // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists - if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) + if (!resource.data + || resource.type !== Resource.TYPE.JSON + || !resource.data.frames + || this.resources[imageResourceName] + ) { next(); @@ -23,6 +27,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // Prepend url path unless the resource image is a data url diff --git a/src/loaders/textureParser.js b/src/loaders/textureParser.js index 6bcbee6..5398a7f 100644 --- a/src/loaders/textureParser.js +++ b/src/loaders/textureParser.js @@ -1,11 +1,12 @@ import * as core from '../core'; +import { Resource } from 'resource-loader'; export default function () { return function textureParser(resource, next) { // create a new texture if the data is an Image object - if (resource.data && resource.isImage) + if (resource.data && resource.type === Resource.TYPE.IMAGE) { const baseTexture = new core.BaseTexture(resource.data, null, core.utils.getResolutionOfUrl(resource.url)); diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index bbb0938..2e7addc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -35,7 +35,8 @@ * * @member {Float32Array} */ - this.uvs = uvs || new Float32Array([0, 0, + this.uvs = uvs || new Float32Array([ + 0, 0, 1, 0, 1, 1, 0, 1]); @@ -45,7 +46,8 @@ * * @member {Float32Array} */ - this.vertices = vertices || new Float32Array([0, 0, + this.vertices = vertices || new Float32Array([ + 0, 0, 100, 0, 100, 100, 0, 100]); diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index e6a0ed5..42048a8 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -237,7 +237,7 @@ continue; } - const frame = child.texture.frame; + const frame = child._texture.frame; context.globalAlpha = this.worldAlpha * child.alpha; @@ -305,10 +305,10 @@ finalHeight = frame.height; } - const resolution = child.texture.baseTexture.resolution; + const resolution = child._texture.baseTexture.resolution; context.drawImage( - child.texture.baseTexture.source, + child._texture.baseTexture.source, frame.x * resolution, frame.y * resolution, frame.width * resolution, diff --git a/test/core/Graphics.js b/test/core/Graphics.js index 0622cfc..50f775b 100644 --- a/test/core/Graphics.js +++ b/test/core/Graphics.js @@ -191,4 +191,21 @@ expect(graphics.currentPath).to.be.null; }); }); + + describe('_calculateBounds', () => + { + it('should only call updateLocalBounds once', () => + { + const graphics = new PIXI.Graphics(); + const spy = sinon.spy(graphics, 'updateLocalBounds'); + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + }); + }); }); diff --git a/test/core/Text.js b/test/core/Text.js index 4f67752..b8ce561 100644 --- a/test/core/Text.js +++ b/test/core/Text.js @@ -2,6 +2,40 @@ describe('PIXI.Text', function () { + describe('getFontStyle', function () + { + it('should be a valid API', function () + { + expect(PIXI.Text.getFontStyle).to.be.a.function; + }); + + it('should assume pixel fonts', function () + { + const style = PIXI.Text.getFontStyle({ fontSize: 72 }); + + expect(style).to.be.a.string; + expect(style).to.have.string(' 72px '); + }); + + it('should handle multiple fonts as array', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: ['Georgia', 'Arial', 'sans-serif'], + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + + it('should handle multiple fonts as string', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: 'Georgia, "Arial", sans-serif', + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + }); + describe('destroy', function () { it('should call through to Sprite.destroy', function () diff --git a/test/index.js b/test/index.js index 50a85da..9689aa0 100755 --- a/test/index.js +++ b/test/index.js @@ -13,6 +13,7 @@ }); require('./core'); require('./interaction'); + require('./loaders'); require('./renders'); require('./prepare'); }); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js new file mode 100644 index 0000000..47d6819 --- /dev/null +++ b/test/interaction/InteractionManager.js @@ -0,0 +1,84 @@ +'use strict'; + +const MockPointer = require('./MockPointer'); + +describe('PIXI.interaction.InteractionManager', function () +{ + describe('onClick', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); + + describe('onTap', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); +}); diff --git a/test/interaction/MockPointer.js b/test/interaction/MockPointer.js new file mode 100644 index 0000000..5bac0d3 --- /dev/null +++ b/test/interaction/MockPointer.js @@ -0,0 +1,115 @@ +'use strict'; + +/** + * Use this to mock mouse/touch/pointer events + * + * @class + */ +class MockPointer { + /** + * @param {PIXI.Container} stage - The root of the scene tree + * @param {number} [width=100] - Width of the renderer + * @param {number} [height=100] - Height of the renderer + */ + constructor(stage, width, height) + { + this.stage = stage; + this.renderer = new PIXI.CanvasRenderer(width || 100, height || 100); + this.renderer.sayHello = () => { /* empty */ }; + this.interaction = this.renderer.plugins.interaction; + } + + /** + * @private + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + setPosition(x, y) + { + this.renderer.plugins.interaction.mapPositionToPoint = (point) => + { + point.x = x; + point.y = y; + }; + } + + /** + * @private + */ + render() + { + this.renderer.render(this.stage); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + click(x, y) + { + this.mousedown(x, y); + this.mouseup(x, y); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + mousedown(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onMouseDown({ clientX: 0, clientY: 0, preventDefault: sinon.stub() }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + mouseup(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onMouseUp({ clientX: 0, clientY: 0, preventDefault: sinon.stub() }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + tap(x, y) + { + this.touchstart(x, y); + this.touchend(x, y); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + touchstart(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onTouchStart({ + preventDefault: sinon.stub(), + changedTouches: [new Touch({ identifier: 0, target: this.renderer.view })], + }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + touchend(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onTouchEnd({ + preventDefault: sinon.stub(), + changedTouches: [new Touch({ identifier: 0, target: this.renderer.view })], + }); + } +} + +module.exports = MockPointer; diff --git a/test/interaction/index.js b/test/interaction/index.js index 9c88370..fd84330 100644 --- a/test/interaction/index.js +++ b/test/interaction/index.js @@ -1,3 +1,4 @@ 'use strict'; require('./InteractionData'); +require('./InteractionManager'); diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js new file mode 100644 index 0000000..7c77343 --- /dev/null +++ b/test/loaders/bitmapFontParser.js @@ -0,0 +1,46 @@ +'use strict'; + +describe('PIXI.loaders.bitmapFontParser', function () +{ + it('should exist and return a function', function () + { + expect(PIXI.loaders.bitmapFontParser).to.be.a('function'); + expect(PIXI.loaders.bitmapFontParser()).to.be.a('function'); + }); + + it('should do nothing if the resource is not XML', function () + { + const spy = sinon.spy(); + const res = {}; + + PIXI.loaders.bitmapFontParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.textures).to.be.undefined; + }); + + it('should do nothing if the resource is not properly formatted XML', function () + { + const spy = sinon.spy(); + const res = { data: document.createDocumentFragment() }; + + PIXI.loaders.bitmapFontParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.textures).to.be.undefined; + }); + + // TODO: Test the texture cache code path. + // TODO: Test the loading texture code path. + // TODO: Test data-url code paths. +}); + +describe('PIXI.loaders.parseBitmapFontData', function () +{ + it('should exist', function () + { + expect(PIXI.loaders.parseBitmapFontData).to.be.a('function'); + }); + + // TODO: Test the parser code. +}); diff --git a/test/loaders/index.js b/test/loaders/index.js new file mode 100644 index 0000000..97fc849 --- /dev/null +++ b/test/loaders/index.js @@ -0,0 +1,6 @@ +'use strict'; + +require('./bitmapFontParser'); +require('./loader'); +require('./spritesheetParser'); +require('./textureParser'); diff --git a/test/loaders/loader.js b/test/loaders/loader.js new file mode 100644 index 0000000..8ec6793 --- /dev/null +++ b/test/loaders/loader.js @@ -0,0 +1,9 @@ +'use strict'; + +describe('PIXI.loaders.Loader', function () +{ + it('should exist', function () + { + expect(PIXI.loaders.Loader).to.be.a('function'); + }); +}); diff --git a/test/loaders/spritesheetParser.js b/test/loaders/spritesheetParser.js new file mode 100644 index 0000000..79f8c15 --- /dev/null +++ b/test/loaders/spritesheetParser.js @@ -0,0 +1,178 @@ +'use strict'; + +const path = require('path'); + +describe('PIXI.loaders.spritesheetParser', function () +{ + it('should exist and return a function', function () + { + expect(PIXI.loaders.spritesheetParser).to.be.a('function'); + expect(PIXI.loaders.spritesheetParser()).to.be.a('function'); + }); + + it('should do nothing if the resource is not JSON', function () + { + const spy = sinon.spy(); + const res = {}; + + PIXI.loaders.spritesheetParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.textures).to.be.undefined; + }); + + it('should do nothing if the resource is JSON, but improper format', function () + { + const spy = sinon.spy(); + const res = createMockResource(PIXI.loaders.Resource.TYPE.JSON, {}); + + PIXI.loaders.spritesheetParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.textures).to.be.undefined; + }); + + it('should load the image & create textures if json is properly formatted', function () + { + const spy = sinon.spy(); + const res = createMockResource(PIXI.loaders.Resource.TYPE.JSON, getJsonSpritesheet()); + const loader = new PIXI.loaders.Loader(); + const addStub = sinon.stub(loader, 'add'); + const imgRes = createMockResource(PIXI.loaders.Resource.TYPE.IMAGE, new Image()); + + imgRes.texture = new PIXI.Texture(new PIXI.BaseTexture(imgRes.data)); + + addStub.yields(imgRes); + + PIXI.loaders.spritesheetParser().call(loader, res, spy); + + addStub.restore(); + + expect(spy).to.have.been.calledOnce; + expect(addStub).to.have.been.calledWith( + `${res.name}_image`, + `${path.dirname(res.url)}/${res.data.meta.image}` + ); + expect(res).to.have.property('textures') + .that.is.an('object') + .with.keys(Object.keys(getJsonSpritesheet().frames)) + .and.has.property('0.png') + .that.is.an.instanceof(PIXI.Texture); + }); + + // TODO: Test that rectangles are created correctly. + // TODO: Test that bathc processing works correctly. + // TODO: Test that resolution processing works correctly. + // TODO: Test that metadata is honored. + // TODO: Test data-url code paths. +}); + +function createMockResource(type, data) +{ + const name = `${Math.floor(Date.now() * Math.random())}`; + + return { + url: `http://localhost/doesnt_exist/${name}`, + name, + type, + data, + metadata: {}, + }; +} + +function getJsonSpritesheet() +{ + /* eslint-disable */ + return {"frames": { + "0.png": + { + "frame": {"x":14,"y":28,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "1.png": + { + "frame": {"x":14,"y":42,"w":12,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":12,"h":14}, + "sourceSize": {"w":12,"h":14} + }, + "2.png": + { + "frame": {"x":14,"y":14,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "3.png": + { + "frame": {"x":42,"y":0,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "4.png": + { + "frame": {"x":28,"y":0,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "5.png": + { + "frame": {"x":14,"y":0,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "6.png": + { + "frame": {"x":0,"y":42,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "7.png": + { + "frame": {"x":0,"y":28,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "8.png": + { + "frame": {"x":0,"y":14,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "9.png": + { + "frame": {"x":0,"y":0,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }}, + "meta": { + "app": "http://www.texturepacker.com", + "version": "1.0", + "image": "hud.png", + "format": "RGBA8888", + "size": {"w":64,"h":64}, + "scale": "1", + "smartupdate": "$TexturePacker:SmartUpdate:47025c98c8b10634b75172d4ed7e7edc$" + } + }; + /* eslint-enable */ +} diff --git a/.gitignore b/.gitignore index 1c378a5..914de54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,12 @@ !.gitkeep *__temp node_modules -docs/ -examples_old/ -bin/ -coverage/ -lib/ -dist/ +/docs +/examples_old +/bin +/coverage +/lib +/dist # jetBrains IDE ignores .idea \ No newline at end of file diff --git a/package.json b/package.json index 844f742..096ae2a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "ismobilejs": "^0.4.0", "object-assign": "^4.0.1", "pixi-gl-core": "^1.0.3", - "resource-loader": "^1.8.0" + "resource-loader": "^2.0.3" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/scripts/jsdoc-fix.js b/scripts/jsdoc-fix.js index 3f239b3..8f6b766 100644 --- a/scripts/jsdoc-fix.js +++ b/scripts/jsdoc-fix.js @@ -7,6 +7,14 @@ const rgxGross = /(\/\*{2}[\W\w]+?\*\/)\s*export\s+default\s+class\s+([^\s]*)/g; const grossReplace = 'export default $2;\n\n$1\nclass $2'; +// JSDoc has issues with expressing member properties within a class +// this is another terrible hack to address this issue. +// See: https://github.com/jsdoc3/jsdoc/issues/1301 + +const rgxMember = /(\@member \{[^\}]+\})(\n[^\/]+\/[\b\s]+)(this\.([^\s]+))/g; +const rgxClassName = /export (default )?class (.+?)\s/; +const rgxNamespace = /\@memberof ([\.a-zA-Z0-9]+)\s/; + exports.handlers = { /** * Called before parsing a file, giving us a change to replace the source. @@ -17,6 +25,17 @@ */ beforeParse(e) { + const namespace = e.source.match(rgxNamespace); + const className = e.source.match(rgxClassName); + + // Fix members not showing up attached to class + if (namespace && className) + { + // console.log(`${namespace[1]}.${className[2]}`); + // Replaces "@member {Type}"" with "@member {Type} PIXI.ClassName#prop" + e.source = e.source.replace(rgxMember, `$1 ${namespace[1]}.${className[2]}#$4$2$3`); + } + e.source = e.source.replace(rgxGross, grossReplace); }, }; diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js index 6f0a417..45f7ab1 100644 --- a/src/core/graphics/Graphics.js +++ b/src/core/graphics/Graphics.js @@ -699,6 +699,9 @@ this.graphicsData.length = 0; } + this.currentPath = null; + this._spriteRect = null; + return this; } @@ -833,7 +836,6 @@ this.boundsDirty = this.dirty; this.updateLocalBounds(); - this.dirty++; this.cachedSpriteDirty = true; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 369b181..ccce680 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -121,6 +121,8 @@ this.emit('prerender'); + const rootResolution = this.resolution; + if (renderTexture) { renderTexture = renderTexture.baseTexture || renderTexture; @@ -207,10 +209,34 @@ displayObject.renderCanvas(this); this.context = tempContext; + this.resolution = rootResolution; + this.emit('postrender'); } /** + * Clear the canvas of renderer. + * + * @param {string} [clearColor] - Clear the canvas with this color, except the canvas is transparent. + */ + clear(clearColor) + { + const context = this.context; + + clearColor = clearColor || this._backgroundColorString; + + if (!this.transparent && clearColor) + { + context.fillStyle = clearColor; + context.fillRect(0, 0, this.width, this.height); + } + else + { + context.clearRect(0, 0, this.width, this.height); + } + } + + /** * Sets the blend mode of the renderer. * * @param {number} blendMode - See {@link PIXI.BLEND_MODES} for valid values. diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js index 8a3be33..ee76814 100644 --- a/src/core/renderers/webgl/TextureGarbageCollector.js +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -95,7 +95,8 @@ { const tm = this.renderer.textureManager; - if (displayObject._texture) + // only destroy non generated textures + if (displayObject._texture && displayObject._texture._glRenderTargets) { tm.destroyTexture(displayObject._texture, true); } diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index ba892bd..b685c1a 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -68,7 +68,6 @@ { // assume it good! // texture = texture.baseTexture || texture; - location = location || 0; const gl = this.gl; @@ -79,6 +78,29 @@ return null; } + const boundTextures = this.renderer.boundTextures; + + // if the location is undefined then this may have been called by n event. + // this being the case the texture may already be bound to a slot. As a texture can only be bound once + // we need to find its current location if it exists. + if (location === undefined) + { + location = 0; + + // TODO maybe we can use texture bound ids later on... + // check if texture is already bound.. + for (let i = 0; i < boundTextures.length; ++i) + { + if (boundTextures[i] === texture) + { + location = i; + break; + } + } + } + + boundTextures[location] = texture; + gl.activeTexture(gl.TEXTURE0 + location); let glTexture = texture._glTextures[this.renderer.CONTEXT_UID]; @@ -158,8 +180,6 @@ glTexture.upload(texture.source); } - this.renderer.boundTextures[location] = texture; - return glTexture; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index b76ae09..ce0e1c4 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -145,16 +145,6 @@ */ this.boundTextures = null; - this._initContext(); - /** - * Manages the filters. - * - * @member {PIXI.FilterManager} - */ - this.filterManager = new FilterManager(this); - // map some webGL blend and drawmodes.. - this.drawModes = mapWebGLDrawModesToPixi(this.gl); - /** * Holds the current shader * @@ -171,6 +161,17 @@ */ this._activeRenderTarget = null; + this._initContext(); + + /** + * Manages the filters. + * + * @member {PIXI.FilterManager} + */ + this.filterManager = new FilterManager(this); + // map some webGL blend and drawmodes.. + this.drawModes = mapWebGLDrawModesToPixi(this.gl); + this._nextTextureLocation = 0; this.setBlendMode(0); diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index b1c5968..64f5d9d 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -131,11 +131,10 @@ // bind the render target to draw the shape in the top corner.. renderTarget.setFrame(destinationFrame, sourceFrame); + // bind the render target renderer.bindRenderTarget(renderTarget); - - // clear the renderTarget - renderer.clear();// [0.5,0.5,0.5, 1.0]); + renderTarget.clear(); } /** @@ -170,6 +169,9 @@ flop.setFrame(currentState.destinationFrame, currentState.sourceFrame); + // finally lets clear the render target before drawing to it.. + flop.clear(); + let i = 0; for (i = 0; i < filters.length - 1; ++i) @@ -182,7 +184,7 @@ flop = t; } - filters[i].apply(this, flip, lastState.renderTarget, false); + filters[i].apply(this, flip, lastState.renderTarget, true); this.freePotRenderTarget(flip); this.freePotRenderTarget(flop); @@ -336,7 +338,9 @@ // rather than a renderTarget const gl = this.renderer.gl; + this.renderer.boundTextures[textureCount] = this.renderer.emptyTextures[textureCount]; gl.activeTexture(gl.TEXTURE0 + textureCount); + uniforms[i].texture.bind(); } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 025fef0..f6b8f31 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -479,7 +479,7 @@ */ get width() { - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -491,7 +491,7 @@ { const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -503,7 +503,7 @@ */ get height() { - return Math.abs(this.scale.y) * this.texture.orig.height; + return Math.abs(this.scale.y) * this._texture.orig.height; } /** @@ -515,7 +515,7 @@ { const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 6368368..8f53300 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -44,7 +44,7 @@ this.vertByteSize = this.vertSize * 4; /** - * The number of images in the SpriteBatch before it flushes. + * The number of images in the SpriteRenderer before it flushes. * * @member {number} */ @@ -168,7 +168,7 @@ // get the uvs for the texture // if the uvs have not updated then no point rendering just yet! - if (!sprite.texture._uvs) + if (!sprite._texture._uvs) { return; } @@ -351,7 +351,7 @@ uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (sprite.worldAlpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; @@ -363,7 +363,7 @@ if (!settings.CAN_UPLOAD_SAME_BUFFER) { // this is still needed for IOS performance.. - // it really does not like uploading to the same buffer in a single frame! + // it really does not like uploading to the same buffer in a single frame! if (this.vaoMax <= this.vertexCount) { this.vaoMax++; @@ -452,7 +452,7 @@ } /** - * Destroys the SpriteBatch. + * Destroys the SpriteRenderer. * */ destroy() diff --git a/src/core/text/Text.js b/src/core/text/Text.js index 4e94f6b..3aa3182 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -32,10 +32,11 @@ /** * @param {string} text - The string that you would like the text to display * @param {object|PIXI.TextStyle} [style] - The style parameters + * @param {HTMLCanvasElement} [canvas] - The canvas element for drawing text */ - constructor(text, style) + constructor(text, style, canvas) { - const canvas = document.createElement('canvas'); + canvas = canvas || document.createElement('canvas'); canvas.width = 3; canvas.height = 3; @@ -606,7 +607,7 @@ { this.updateText(true); - return Math.abs(this.scale.x) * this.texture.orig.width; + return Math.abs(this.scale.x) * this._texture.orig.width; } /** @@ -620,7 +621,7 @@ const s = sign(this.scale.x) || 1; - this.scale.x = s * value / this.texture.orig.width; + this.scale.x = s * value / this._texture.orig.width; this._width = value; } @@ -648,7 +649,7 @@ const s = sign(this.scale.y) || 1; - this.scale.y = s * value / this.texture.orig.height; + this.scale.y = s * value / this._texture.orig.height; this._height = value; } @@ -704,8 +705,7 @@ */ set text(text) { - text = text || ' '; - text = text.toString(); + text = String(text || ' '); if (this._text === text) { @@ -735,7 +735,29 @@ // build canvas api font setting from individual components. Convert a numeric style.fontSize to px const fontSizeString = (typeof style.fontSize === 'number') ? `${style.fontSize}px` : style.fontSize; - return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} "${style.fontFamily}"`; + // Clean-up fontFamily property by quoting each font name + // this will support font names with spaces + let fontFamilies = style.fontFamily; + + if (!Array.isArray(style.fontFamily)) + { + fontFamilies = style.fontFamily.split(','); + } + + for (let i = fontFamilies.length - 1; i >= 0; i--) + { + // Trim any extra white-space + let fontFamily = fontFamilies[i].trim(); + + // Check if font already contains strings + if (!(/([\"\'])[^\'\"]+\1/).test(fontFamily)) + { + fontFamily = `"${fontFamily}"`; + } + fontFamilies[i] = fontFamily; + } + + return `${style.fontStyle} ${style.fontVariant} ${style.fontWeight} ${fontSizeString} ${fontFamilies.join(',')}`; } /** diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index faeb91c..3b25528 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -57,7 +57,7 @@ * {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle|MDN} * @param {number} [style.fillGradientType=PIXI.TEXT_GRADIENT.LINEAR_VERTICAL] - If fills styles are * supplied, this can change the type/direction of the gradient. See {@link PIXI.TEXT_GRADIENT} for possible values - * @param {string} [style.fontFamily='Arial'] - The font family + * @param {string|string[]} [style.fontFamily='Arial'] - The font family * @param {number|string} [style.fontSize=26] - The font size (as a number it converts to px, but as a string, * equivalents are '26px','20pt','160%' or '1.6em') * @param {string} [style.fontStyle='normal'] - The font style ('normal', 'italic' or 'oblique') diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index b197e9c..3066a3a 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -517,7 +517,7 @@ */ get width() { - return this.orig ? this.orig.width : 0; + return this.orig.width; } /** @@ -527,7 +527,7 @@ */ get height() { - return this.orig ? this.orig.height : 0; + return this.orig.height; } } diff --git a/src/deprecation.js b/src/deprecation.js index e52d60b..045a19f 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -4,6 +4,7 @@ import * as extras from './extras'; import * as filters from './filters'; import * as prepare from './prepare'; +import * as loaders from './loaders'; // provide method to give a stack track for warnings // useful for tracking-down where deprecated methods/properties/classes @@ -940,3 +941,65 @@ return NaN; }, }); + +Object.defineProperties(loaders.Resource.prototype, { + isJson: { + get() + { + warn('The isJson property is deprecated, please use `resource.type === Resource.TYPE.JSON`.'); + + return this.type === loaders.Loader.Resource.TYPE.JSON; + }, + }, + isXml: { + get() + { + warn('The isXml property is deprecated, please use `resource.type === Resource.TYPE.XML`.'); + + return this.type === loaders.Loader.Resource.TYPE.XML; + }, + }, + isImage: { + get() + { + warn('The isImage property is deprecated, please use `resource.type === Resource.TYPE.IMAGE`.'); + + return this.type === loaders.Loader.Resource.TYPE.IMAGE; + }, + }, + isAudio: { + get() + { + warn('The isAudio property is deprecated, please use `resource.type === Resource.TYPE.AUDIO`.'); + + return this.type === loaders.Loader.Resource.TYPE.AUDIO; + }, + }, + isVideo: { + get() + { + warn('The isVideo property is deprecated, please use `resource.type === Resource.TYPE.VIDEO`.'); + + return this.type === loaders.Loader.Resource.TYPE.VIDEO; + }, + }, +}); + +Object.defineProperties(loaders.Loader.prototype, { + before: { + get() + { + warn('The before() method is deprecated, please use pre().'); + + return this.pre; + }, + }, + after: { + get() + { + warn('The after() method is deprecated, please use use().'); + + return this.use; + }, + }, +}); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 56b3819..94e18de 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -37,22 +37,20 @@ super(); /** - * The width of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the width of the overall text * * @member {number} - * @readonly + * @private */ - this.textWidth = 0; + this._textWidth = 0; /** - * The height of the overall text, different from fontSize, - * which is defined in the style object + * Private tracker for the height of the overall text * * @member {number} - * @readonly + * @private */ - this.textHeight = 0; + this._textHeight = 0; /** * Private tracker for the letter sprite pool. @@ -264,16 +262,16 @@ this.removeChild(this._glyphs[i]); } - this.textWidth = maxLineWidth * scale; - this.textHeight = (pos.y + data.lineHeight) * scale; + this._textWidth = maxLineWidth * scale; + this._textHeight = (pos.y + data.lineHeight) * scale; // apply anchor if (this.anchor.x !== 0 || this.anchor.y !== 0) { for (let i = 0; i < lenChars; i++) { - this._glyphs[i].x -= this.textWidth * this.anchor.x; - this._glyphs[i].y -= this.textHeight * this.anchor.y; + this._glyphs[i].x -= this._textWidth * this.anchor.x; + this._glyphs[i].y -= this._textHeight * this.anchor.y; } } this.maxLineHeight = maxLineHeight * scale; @@ -459,6 +457,36 @@ this._text = value; this.dirty = true; } + + /** + * The width of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textWidth() + { + this.validate(); + + return this._textWidth; + } + + /** + * The height of the overall text, different from fontSize, + * which is defined in the style object + * + * @member {number} + * @memberof PIXI.extras.BitmapText# + * @readonly + */ + get textHeight() + { + this.validate(); + + return this._textHeight; + } } BitmapText.fonts = {}; diff --git a/src/extras/TextureTransform.js b/src/extras/TextureTransform.js index 88648b6..bbc6a8e 100644 --- a/src/extras/TextureTransform.js +++ b/src/extras/TextureTransform.js @@ -4,6 +4,9 @@ /** * class controls uv transform and frame clamp for texture + * + * @class + * @memberof PIXI.extras */ export default class TextureTransform { /** @@ -27,7 +30,7 @@ /** * Changes frame clamping * Works with TilingSprite and Mesh - * Change to 1.5 if you tex ture has repeated right and bottom lines, that leads to smoother borders + * Change to 1.5 if you texture has repeated right and bottom lines, that leads to smoother borders * * @default 0 * @member {number} @@ -71,7 +74,7 @@ */ update(forceUpdate) { - const tex = this.texture; + const tex = this._texture; if (!tex || !tex.valid) { @@ -79,14 +82,14 @@ } if (!forceUpdate - && this._lastTextureID === this.texture._updateID) + && this._lastTextureID === tex._updateID) { return; } - this._lastTextureID = this.texture._updateID; + this._lastTextureID = tex._updateID; - const uvs = this.texture._uvs; + const uvs = tex._uvs; this.mapCoord.set(uvs.x1 - uvs.x0, uvs.y1 - uvs.y0, uvs.x3 - uvs.x0, uvs.y3 - uvs.y0, uvs.x0, uvs.y0); diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js index 05f6628..b18cd35 100644 --- a/src/interaction/InteractionManager.js +++ b/src/interaction/InteractionManager.js @@ -15,6 +15,7 @@ * The interaction manager deals with mouse and touch events. Any DisplayObject can be interactive * if its interactive parameter is set to true * This manager also supports multitouch. + * By default, an instance of this class is automatically created, and can be found at renderer.plugins.interaction * * @class * @extends EventEmitter @@ -53,7 +54,7 @@ this.autoPreventDefault = options.autoPreventDefault !== undefined ? options.autoPreventDefault : true; /** - * As this frequency increases the interaction events will be checked more often. + * Frequency in milliseconds that the mousemove, moveover & mouseout interaction events will be checked. * * @member {number} * @default 10 @@ -105,7 +106,7 @@ this.interactionDOMElement = null; /** - * This property determines if mousemove and touchmove events are fired only when the cursror + * This property determines if mousemove and touchmove events are fired only when the cursor * is over the object. * Setting to true will make things work more in line with how the DOM verison works. * Setting to false can make things easier for things like dragging diff --git a/src/loaders/bitmapFontParser.js b/src/loaders/bitmapFontParser.js index 94a7cbc..cc2618c 100644 --- a/src/loaders/bitmapFontParser.js +++ b/src/loaders/bitmapFontParser.js @@ -65,7 +65,7 @@ return function bitmapFontParser(resource, next) { // skip if no data or not xml data - if (!resource.data || !resource.isXml) + if (!resource.data || resource.type !== Resource.TYPE.XML) { next(); @@ -125,6 +125,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // load the texture for the font diff --git a/src/loaders/loader.js b/src/loaders/loader.js index 978aa2d..0a670cb 100644 --- a/src/loaders/loader.js +++ b/src/loaders/loader.js @@ -1,4 +1,6 @@ import ResourceLoader from 'resource-loader'; +import { blobMiddlewareFactory } from 'resource-loader/lib/middlewares/parsing/blob'; +import EventEmitter from 'eventemitter3'; import textureParser from './textureParser'; import spritesheetParser from './spritesheetParser'; import bitmapFontParser from './bitmapFontParser'; @@ -36,11 +38,19 @@ constructor(baseUrl, concurrency) { super(baseUrl, concurrency); + EventEmitter.call(this); for (let i = 0; i < Loader._pixiMiddleware.length; ++i) { this.use(Loader._pixiMiddleware[i]()); } + + // Compat layer, translate the new v2 signals into old v1 events. + this.onStart.add((l) => this.emit('start', l)); + this.onProgress.add((l, r) => this.emit('progress', l, r)); + this.onError.add((e, l, r) => this.emit('error', e, l, r)); + this.onLoad.add((l, r) => this.emit('load', l, r)); + this.onComplete.add((l, r) => this.emit('complete', l, r)); } /** @@ -55,9 +65,15 @@ } } +// Copy EE3 prototype (mixin) +for (const k in EventEmitter.prototype) +{ + Loader.prototype[k] = EventEmitter.prototype[k]; +} + Loader._pixiMiddleware = [ // parse any blob into more usable objects (e.g. Image) - ResourceLoader.middleware.parsing.blob, + blobMiddlewareFactory, // parse any Image objects into textures textureParser, // parse any spritesheet data into multiple textures diff --git a/src/loaders/spritesheetParser.js b/src/loaders/spritesheetParser.js index 2b30b1c..7570332 100644 --- a/src/loaders/spritesheetParser.js +++ b/src/loaders/spritesheetParser.js @@ -12,7 +12,11 @@ const imageResourceName = `${resource.name}_image`; // skip if no data, its not json, it isn't spritesheet data, or the image resource already exists - if (!resource.data || !resource.isJson || !resource.data.frames || this.resources[imageResourceName]) + if (!resource.data + || resource.type !== Resource.TYPE.JSON + || !resource.data.frames + || this.resources[imageResourceName] + ) { next(); @@ -23,6 +27,7 @@ crossOrigin: resource.crossOrigin, loadType: Resource.LOAD_TYPE.IMAGE, metadata: resource.metadata.imageMetadata, + parentResource: resource, }; // Prepend url path unless the resource image is a data url diff --git a/src/loaders/textureParser.js b/src/loaders/textureParser.js index 6bcbee6..5398a7f 100644 --- a/src/loaders/textureParser.js +++ b/src/loaders/textureParser.js @@ -1,11 +1,12 @@ import * as core from '../core'; +import { Resource } from 'resource-loader'; export default function () { return function textureParser(resource, next) { // create a new texture if the data is an Image object - if (resource.data && resource.isImage) + if (resource.data && resource.type === Resource.TYPE.IMAGE) { const baseTexture = new core.BaseTexture(resource.data, null, core.utils.getResolutionOfUrl(resource.url)); diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index bbb0938..2e7addc 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -35,7 +35,8 @@ * * @member {Float32Array} */ - this.uvs = uvs || new Float32Array([0, 0, + this.uvs = uvs || new Float32Array([ + 0, 0, 1, 0, 1, 1, 0, 1]); @@ -45,7 +46,8 @@ * * @member {Float32Array} */ - this.vertices = vertices || new Float32Array([0, 0, + this.vertices = vertices || new Float32Array([ + 0, 0, 100, 0, 100, 100, 0, 100]); diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index e6a0ed5..42048a8 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -237,7 +237,7 @@ continue; } - const frame = child.texture.frame; + const frame = child._texture.frame; context.globalAlpha = this.worldAlpha * child.alpha; @@ -305,10 +305,10 @@ finalHeight = frame.height; } - const resolution = child.texture.baseTexture.resolution; + const resolution = child._texture.baseTexture.resolution; context.drawImage( - child.texture.baseTexture.source, + child._texture.baseTexture.source, frame.x * resolution, frame.y * resolution, frame.width * resolution, diff --git a/test/core/Graphics.js b/test/core/Graphics.js index 0622cfc..50f775b 100644 --- a/test/core/Graphics.js +++ b/test/core/Graphics.js @@ -191,4 +191,21 @@ expect(graphics.currentPath).to.be.null; }); }); + + describe('_calculateBounds', () => + { + it('should only call updateLocalBounds once', () => + { + const graphics = new PIXI.Graphics(); + const spy = sinon.spy(graphics, 'updateLocalBounds'); + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + + graphics._calculateBounds(); + + expect(spy).to.have.been.calledOnce; + }); + }); }); diff --git a/test/core/Text.js b/test/core/Text.js index 4f67752..b8ce561 100644 --- a/test/core/Text.js +++ b/test/core/Text.js @@ -2,6 +2,40 @@ describe('PIXI.Text', function () { + describe('getFontStyle', function () + { + it('should be a valid API', function () + { + expect(PIXI.Text.getFontStyle).to.be.a.function; + }); + + it('should assume pixel fonts', function () + { + const style = PIXI.Text.getFontStyle({ fontSize: 72 }); + + expect(style).to.be.a.string; + expect(style).to.have.string(' 72px '); + }); + + it('should handle multiple fonts as array', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: ['Georgia', 'Arial', 'sans-serif'], + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + + it('should handle multiple fonts as string', function () + { + const style = PIXI.Text.getFontStyle({ + fontFamily: 'Georgia, "Arial", sans-serif', + }); + + expect(style).to.have.string('"Georgia","Arial","sans-serif"'); + }); + }); + describe('destroy', function () { it('should call through to Sprite.destroy', function () diff --git a/test/index.js b/test/index.js index 50a85da..9689aa0 100755 --- a/test/index.js +++ b/test/index.js @@ -13,6 +13,7 @@ }); require('./core'); require('./interaction'); + require('./loaders'); require('./renders'); require('./prepare'); }); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js new file mode 100644 index 0000000..47d6819 --- /dev/null +++ b/test/interaction/InteractionManager.js @@ -0,0 +1,84 @@ +'use strict'; + +const MockPointer = require('./MockPointer'); + +describe('PIXI.interaction.InteractionManager', function () +{ + describe('onClick', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('click', clickSpy); + + pointer.click(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); + + describe('onTap', function () + { + it('should call handler when inside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(10, 10); + + expect(clickSpy).to.have.been.calledOnce; + }); + + it('should not call handler when outside', function () + { + const stage = new PIXI.Container(); + const graphics = new PIXI.Graphics(); + const clickSpy = sinon.spy(); + const pointer = new MockPointer(stage); + + stage.addChild(graphics); + graphics.beginFill(0xFFFFFF); + graphics.drawRect(0, 0, 50, 50); + graphics.interactive = true; + graphics.on('tap', clickSpy); + + pointer.tap(60, 60); + + expect(clickSpy).to.not.have.been.called; + }); + }); +}); diff --git a/test/interaction/MockPointer.js b/test/interaction/MockPointer.js new file mode 100644 index 0000000..5bac0d3 --- /dev/null +++ b/test/interaction/MockPointer.js @@ -0,0 +1,115 @@ +'use strict'; + +/** + * Use this to mock mouse/touch/pointer events + * + * @class + */ +class MockPointer { + /** + * @param {PIXI.Container} stage - The root of the scene tree + * @param {number} [width=100] - Width of the renderer + * @param {number} [height=100] - Height of the renderer + */ + constructor(stage, width, height) + { + this.stage = stage; + this.renderer = new PIXI.CanvasRenderer(width || 100, height || 100); + this.renderer.sayHello = () => { /* empty */ }; + this.interaction = this.renderer.plugins.interaction; + } + + /** + * @private + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + setPosition(x, y) + { + this.renderer.plugins.interaction.mapPositionToPoint = (point) => + { + point.x = x; + point.y = y; + }; + } + + /** + * @private + */ + render() + { + this.renderer.render(this.stage); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + click(x, y) + { + this.mousedown(x, y); + this.mouseup(x, y); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + mousedown(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onMouseDown({ clientX: 0, clientY: 0, preventDefault: sinon.stub() }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + mouseup(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onMouseUp({ clientX: 0, clientY: 0, preventDefault: sinon.stub() }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + tap(x, y) + { + this.touchstart(x, y); + this.touchend(x, y); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + touchstart(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onTouchStart({ + preventDefault: sinon.stub(), + changedTouches: [new Touch({ identifier: 0, target: this.renderer.view })], + }); + } + + /** + * @param {number} x - pointer x position + * @param {number} y - pointer y position + */ + touchend(x, y) + { + this.setPosition(x, y); + this.render(); + this.interaction.onTouchEnd({ + preventDefault: sinon.stub(), + changedTouches: [new Touch({ identifier: 0, target: this.renderer.view })], + }); + } +} + +module.exports = MockPointer; diff --git a/test/interaction/index.js b/test/interaction/index.js index 9c88370..fd84330 100644 --- a/test/interaction/index.js +++ b/test/interaction/index.js @@ -1,3 +1,4 @@ 'use strict'; require('./InteractionData'); +require('./InteractionManager'); diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js new file mode 100644 index 0000000..7c77343 --- /dev/null +++ b/test/loaders/bitmapFontParser.js @@ -0,0 +1,46 @@ +'use strict'; + +describe('PIXI.loaders.bitmapFontParser', function () +{ + it('should exist and return a function', function () + { + expect(PIXI.loaders.bitmapFontParser).to.be.a('function'); + expect(PIXI.loaders.bitmapFontParser()).to.be.a('function'); + }); + + it('should do nothing if the resource is not XML', function () + { + const spy = sinon.spy(); + const res = {}; + + PIXI.loaders.bitmapFontParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.textures).to.be.undefined; + }); + + it('should do nothing if the resource is not properly formatted XML', function () + { + const spy = sinon.spy(); + const res = { data: document.createDocumentFragment() }; + + PIXI.loaders.bitmapFontParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.textures).to.be.undefined; + }); + + // TODO: Test the texture cache code path. + // TODO: Test the loading texture code path. + // TODO: Test data-url code paths. +}); + +describe('PIXI.loaders.parseBitmapFontData', function () +{ + it('should exist', function () + { + expect(PIXI.loaders.parseBitmapFontData).to.be.a('function'); + }); + + // TODO: Test the parser code. +}); diff --git a/test/loaders/index.js b/test/loaders/index.js new file mode 100644 index 0000000..97fc849 --- /dev/null +++ b/test/loaders/index.js @@ -0,0 +1,6 @@ +'use strict'; + +require('./bitmapFontParser'); +require('./loader'); +require('./spritesheetParser'); +require('./textureParser'); diff --git a/test/loaders/loader.js b/test/loaders/loader.js new file mode 100644 index 0000000..8ec6793 --- /dev/null +++ b/test/loaders/loader.js @@ -0,0 +1,9 @@ +'use strict'; + +describe('PIXI.loaders.Loader', function () +{ + it('should exist', function () + { + expect(PIXI.loaders.Loader).to.be.a('function'); + }); +}); diff --git a/test/loaders/spritesheetParser.js b/test/loaders/spritesheetParser.js new file mode 100644 index 0000000..79f8c15 --- /dev/null +++ b/test/loaders/spritesheetParser.js @@ -0,0 +1,178 @@ +'use strict'; + +const path = require('path'); + +describe('PIXI.loaders.spritesheetParser', function () +{ + it('should exist and return a function', function () + { + expect(PIXI.loaders.spritesheetParser).to.be.a('function'); + expect(PIXI.loaders.spritesheetParser()).to.be.a('function'); + }); + + it('should do nothing if the resource is not JSON', function () + { + const spy = sinon.spy(); + const res = {}; + + PIXI.loaders.spritesheetParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.textures).to.be.undefined; + }); + + it('should do nothing if the resource is JSON, but improper format', function () + { + const spy = sinon.spy(); + const res = createMockResource(PIXI.loaders.Resource.TYPE.JSON, {}); + + PIXI.loaders.spritesheetParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.textures).to.be.undefined; + }); + + it('should load the image & create textures if json is properly formatted', function () + { + const spy = sinon.spy(); + const res = createMockResource(PIXI.loaders.Resource.TYPE.JSON, getJsonSpritesheet()); + const loader = new PIXI.loaders.Loader(); + const addStub = sinon.stub(loader, 'add'); + const imgRes = createMockResource(PIXI.loaders.Resource.TYPE.IMAGE, new Image()); + + imgRes.texture = new PIXI.Texture(new PIXI.BaseTexture(imgRes.data)); + + addStub.yields(imgRes); + + PIXI.loaders.spritesheetParser().call(loader, res, spy); + + addStub.restore(); + + expect(spy).to.have.been.calledOnce; + expect(addStub).to.have.been.calledWith( + `${res.name}_image`, + `${path.dirname(res.url)}/${res.data.meta.image}` + ); + expect(res).to.have.property('textures') + .that.is.an('object') + .with.keys(Object.keys(getJsonSpritesheet().frames)) + .and.has.property('0.png') + .that.is.an.instanceof(PIXI.Texture); + }); + + // TODO: Test that rectangles are created correctly. + // TODO: Test that bathc processing works correctly. + // TODO: Test that resolution processing works correctly. + // TODO: Test that metadata is honored. + // TODO: Test data-url code paths. +}); + +function createMockResource(type, data) +{ + const name = `${Math.floor(Date.now() * Math.random())}`; + + return { + url: `http://localhost/doesnt_exist/${name}`, + name, + type, + data, + metadata: {}, + }; +} + +function getJsonSpritesheet() +{ + /* eslint-disable */ + return {"frames": { + "0.png": + { + "frame": {"x":14,"y":28,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "1.png": + { + "frame": {"x":14,"y":42,"w":12,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":12,"h":14}, + "sourceSize": {"w":12,"h":14} + }, + "2.png": + { + "frame": {"x":14,"y":14,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "3.png": + { + "frame": {"x":42,"y":0,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "4.png": + { + "frame": {"x":28,"y":0,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "5.png": + { + "frame": {"x":14,"y":0,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "6.png": + { + "frame": {"x":0,"y":42,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "7.png": + { + "frame": {"x":0,"y":28,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "8.png": + { + "frame": {"x":0,"y":14,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }, + "9.png": + { + "frame": {"x":0,"y":0,"w":14,"h":14}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, + "sourceSize": {"w":14,"h":14} + }}, + "meta": { + "app": "http://www.texturepacker.com", + "version": "1.0", + "image": "hud.png", + "format": "RGBA8888", + "size": {"w":64,"h":64}, + "scale": "1", + "smartupdate": "$TexturePacker:SmartUpdate:47025c98c8b10634b75172d4ed7e7edc$" + } + }; + /* eslint-enable */ +} diff --git a/test/loaders/textureParser.js b/test/loaders/textureParser.js new file mode 100644 index 0000000..e739e4d --- /dev/null +++ b/test/loaders/textureParser.js @@ -0,0 +1,50 @@ +'use strict'; + +describe('PIXI.loaders.textureParser', function () +{ + it('should exist and return a function', function () + { + expect(PIXI.loaders.textureParser).to.be.a('function'); + expect(PIXI.loaders.textureParser()).to.be.a('function'); + }); + + it('should do nothing if the resource is not an image', function () + { + const spy = sinon.spy(); + const res = {}; + + PIXI.loaders.textureParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.texture).to.be.undefined; + }); + + it('should create a texture if resource is an image', function () + { + const spy = sinon.spy(); + const res = createMockResource(PIXI.loaders.Resource.TYPE.IMAGE, new Image()); + + PIXI.loaders.textureParser()(res, spy); + + expect(spy).to.have.been.calledOnce; + expect(res.texture).to.be.an.instanceof(PIXI.Texture); + + expect(PIXI.utils.BaseTextureCache).to.have.property(res.name, res.texture.baseTexture); + expect(PIXI.utils.BaseTextureCache).to.have.property(res.url, res.texture.baseTexture); + + expect(PIXI.utils.TextureCache).to.have.property(res.name, res.texture); + expect(PIXI.utils.TextureCache).to.have.property(res.url, res.texture); + }); +}); + +function createMockResource(type, data) +{ + const name = `${Math.floor(Date.now() * Math.random())}`; + + return { + url: `http://localhost/doesnt_exist/${name}`, + name, + type, + data, + }; +}