diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 4d95e15..2420a7d 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -266,6 +266,7 @@ reset() { this.runners.reset.run(); + return this; } diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 4d95e15..2420a7d 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -266,6 +266,7 @@ reset() { this.runners.reset.run(); + return this; } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 5e065e2..7cd460c 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -2,6 +2,8 @@ import Program from '../../../shader/Program'; import { BLEND_MODES } from '../../../const'; import settings from '../../../settings'; +import { uid } from '../../../utils'; +import extractUniformsFromSrc from './extractUniformsFromSrc'; // let math = require('../../../math'); /** @@ -49,12 +51,12 @@ this.glShaders = {}; // used for cacheing.. sure there is a better way! - if (!SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) + if (!Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) { - SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); + Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); } - this.glShaderKey = SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; + this.glShaderKey = Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; /** * The padding of the filter. Some filters require extra space to breath such as a blur. @@ -185,3 +187,12 @@ ].join('\n'); } } + +/** + * Used for caching shader IDs + * + * @static + * @private + */ +Filter.SOURCE_KEY_MAP = {}; + diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 4d95e15..2420a7d 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -266,6 +266,7 @@ reset() { this.runners.reset.run(); + return this; } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 5e065e2..7cd460c 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -2,6 +2,8 @@ import Program from '../../../shader/Program'; import { BLEND_MODES } from '../../../const'; import settings from '../../../settings'; +import { uid } from '../../../utils'; +import extractUniformsFromSrc from './extractUniformsFromSrc'; // let math = require('../../../math'); /** @@ -49,12 +51,12 @@ this.glShaders = {}; // used for cacheing.. sure there is a better way! - if (!SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) + if (!Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) { - SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); + Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); } - this.glShaderKey = SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; + this.glShaderKey = Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; /** * The padding of the filter. Some filters require extra space to breath such as a blur. @@ -185,3 +187,12 @@ ].join('\n'); } } + +/** + * Used for caching shader IDs + * + * @static + * @private + */ +Filter.SOURCE_KEY_MAP = {}; + diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js index 5c4ca3f..3843739 100644 --- a/src/core/renderers/webgl/systems/StencilSystem.js +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -125,6 +125,6 @@ { super.destroy(this); - this.stencilMaskStack.stencilStack = null; + this.stencilMaskStack = null; } } diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 4d95e15..2420a7d 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -266,6 +266,7 @@ reset() { this.runners.reset.run(); + return this; } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 5e065e2..7cd460c 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -2,6 +2,8 @@ import Program from '../../../shader/Program'; import { BLEND_MODES } from '../../../const'; import settings from '../../../settings'; +import { uid } from '../../../utils'; +import extractUniformsFromSrc from './extractUniformsFromSrc'; // let math = require('../../../math'); /** @@ -49,12 +51,12 @@ this.glShaders = {}; // used for cacheing.. sure there is a better way! - if (!SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) + if (!Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) { - SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); + Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); } - this.glShaderKey = SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; + this.glShaderKey = Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; /** * The padding of the filter. Some filters require extra space to breath such as a blur. @@ -185,3 +187,12 @@ ].join('\n'); } } + +/** + * Used for caching shader IDs + * + * @static + * @private + */ +Filter.SOURCE_KEY_MAP = {}; + diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js index 5c4ca3f..3843739 100644 --- a/src/core/renderers/webgl/systems/StencilSystem.js +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -125,6 +125,6 @@ { super.destroy(this); - this.stencilMaskStack.stencilStack = null; + this.stencilMaskStack = null; } } diff --git a/src/deprecation.js b/src/deprecation.js index d1b689e..209910a 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -601,6 +601,21 @@ return this.generateCanvasTexture(scaleMode, resolution); }; + /** + * @method + * @name PIXI.GroupD8.isSwapWidthHeight + * @see PIXI.GroupD8.isVertical + * @param {number} rotation - The number to check. + * @returns {boolean} Whether or not the direction is vertical + * @deprecated since version 4.6.0 + */ + core.GroupD8.isSwapWidthHeight = function isSwapWidthHeight(rotation) + { + warn('GroupD8.isSwapWidthHeight was renamed to GroupD8.isVertical'); + + return core.GroupD8.isVertical(rotation); + }; + core.RenderTexture.prototype.render = function render(displayObject, matrix, clear, updateTransform) { this.legacyRenderer.render(displayObject, this, clear, matrix, !updateTransform); diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 4d95e15..2420a7d 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -266,6 +266,7 @@ reset() { this.runners.reset.run(); + return this; } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 5e065e2..7cd460c 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -2,6 +2,8 @@ import Program from '../../../shader/Program'; import { BLEND_MODES } from '../../../const'; import settings from '../../../settings'; +import { uid } from '../../../utils'; +import extractUniformsFromSrc from './extractUniformsFromSrc'; // let math = require('../../../math'); /** @@ -49,12 +51,12 @@ this.glShaders = {}; // used for cacheing.. sure there is a better way! - if (!SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) + if (!Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) { - SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); + Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); } - this.glShaderKey = SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; + this.glShaderKey = Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; /** * The padding of the filter. Some filters require extra space to breath such as a blur. @@ -185,3 +187,12 @@ ].join('\n'); } } + +/** + * Used for caching shader IDs + * + * @static + * @private + */ +Filter.SOURCE_KEY_MAP = {}; + diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js index 5c4ca3f..3843739 100644 --- a/src/core/renderers/webgl/systems/StencilSystem.js +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -125,6 +125,6 @@ { super.destroy(this); - this.stencilMaskStack.stencilStack = null; + this.stencilMaskStack = null; } } diff --git a/src/deprecation.js b/src/deprecation.js index d1b689e..209910a 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -601,6 +601,21 @@ return this.generateCanvasTexture(scaleMode, resolution); }; + /** + * @method + * @name PIXI.GroupD8.isSwapWidthHeight + * @see PIXI.GroupD8.isVertical + * @param {number} rotation - The number to check. + * @returns {boolean} Whether or not the direction is vertical + * @deprecated since version 4.6.0 + */ + core.GroupD8.isSwapWidthHeight = function isSwapWidthHeight(rotation) + { + warn('GroupD8.isSwapWidthHeight was renamed to GroupD8.isVertical'); + + return core.GroupD8.isVertical(rotation); + }; + core.RenderTexture.prototype.render = function render(displayObject, matrix, clear, updateTransform) { this.legacyRenderer.render(displayObject, this, clear, matrix, !updateTransform); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 0592b54..96d9f44 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -1,5 +1,6 @@ import * as core from '../core'; import ObservablePoint from '../core/math/ObservablePoint'; +import { getResolutionOfUrl } from '../core/utils'; import settings from '../core/settings'; /** @@ -510,7 +511,8 @@ const data = {}; const info = xml.getElementsByTagName('info')[0]; const common = xml.getElementsByTagName('common')[0]; - const res = texture.baseTexture.resolution || settings.RESOLUTION; + const fileName = xml.getElementsByTagName('page')[0].getAttribute('file'); + const res = getResolutionOfUrl(fileName, settings.RESOLUTION); data.font = info.getAttribute('face'); data.size = parseInt(info.getAttribute('size'), 10); diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 4d95e15..2420a7d 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -266,6 +266,7 @@ reset() { this.runners.reset.run(); + return this; } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 5e065e2..7cd460c 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -2,6 +2,8 @@ import Program from '../../../shader/Program'; import { BLEND_MODES } from '../../../const'; import settings from '../../../settings'; +import { uid } from '../../../utils'; +import extractUniformsFromSrc from './extractUniformsFromSrc'; // let math = require('../../../math'); /** @@ -49,12 +51,12 @@ this.glShaders = {}; // used for cacheing.. sure there is a better way! - if (!SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) + if (!Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) { - SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); + Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); } - this.glShaderKey = SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; + this.glShaderKey = Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; /** * The padding of the filter. Some filters require extra space to breath such as a blur. @@ -185,3 +187,12 @@ ].join('\n'); } } + +/** + * Used for caching shader IDs + * + * @static + * @private + */ +Filter.SOURCE_KEY_MAP = {}; + diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js index 5c4ca3f..3843739 100644 --- a/src/core/renderers/webgl/systems/StencilSystem.js +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -125,6 +125,6 @@ { super.destroy(this); - this.stencilMaskStack.stencilStack = null; + this.stencilMaskStack = null; } } diff --git a/src/deprecation.js b/src/deprecation.js index d1b689e..209910a 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -601,6 +601,21 @@ return this.generateCanvasTexture(scaleMode, resolution); }; + /** + * @method + * @name PIXI.GroupD8.isSwapWidthHeight + * @see PIXI.GroupD8.isVertical + * @param {number} rotation - The number to check. + * @returns {boolean} Whether or not the direction is vertical + * @deprecated since version 4.6.0 + */ + core.GroupD8.isSwapWidthHeight = function isSwapWidthHeight(rotation) + { + warn('GroupD8.isSwapWidthHeight was renamed to GroupD8.isVertical'); + + return core.GroupD8.isVertical(rotation); + }; + core.RenderTexture.prototype.render = function render(displayObject, matrix, clear, updateTransform) { this.legacyRenderer.render(displayObject, this, clear, matrix, !updateTransform); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 0592b54..96d9f44 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -1,5 +1,6 @@ import * as core from '../core'; import ObservablePoint from '../core/math/ObservablePoint'; +import { getResolutionOfUrl } from '../core/utils'; import settings from '../core/settings'; /** @@ -510,7 +511,8 @@ const data = {}; const info = xml.getElementsByTagName('info')[0]; const common = xml.getElementsByTagName('common')[0]; - const res = texture.baseTexture.resolution || settings.RESOLUTION; + const fileName = xml.getElementsByTagName('page')[0].getAttribute('file'); + const res = getResolutionOfUrl(fileName, settings.RESOLUTION); data.font = info.getAttribute('face'); data.size = parseInt(info.getAttribute('size'), 10); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js index 3f84d6a..136640e 100644 --- a/test/interaction/InteractionManager.js +++ b/test/interaction/InteractionManager.js @@ -279,13 +279,48 @@ removeSpy.restore(); }); - it('should add and remove pointer events to element', function () + it('should add and remove pointer events to element seven times when touch events are supported', function () { const manager = new PIXI.interaction.InteractionManager(sinon.stub()); const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; manager.interactionDOMElement = element; manager.supportsPointerEvents = true; + manager.supportsTouchEvents = true; + + manager.addEvents(); + + expect(element.addEventListener).to.have.been.callCount(7); + expect(element.addEventListener).to.have.been.calledWith('pointerdown'); + expect(element.addEventListener).to.have.been.calledWith('pointerleave'); + expect(element.addEventListener).to.have.been.calledWith('pointerover'); + + expect(element.addEventListener).to.have.been.calledWith('touchstart'); + expect(element.addEventListener).to.have.been.calledWith('touchcancel'); + expect(element.addEventListener).to.have.been.calledWith('touchend'); + expect(element.addEventListener).to.have.been.calledWith('touchmove'); + + manager.removeEvents(); + + expect(element.removeEventListener).to.have.been.callCount(7); + expect(element.removeEventListener).to.have.been.calledWith('pointerdown'); + expect(element.removeEventListener).to.have.been.calledWith('pointerleave'); + expect(element.removeEventListener).to.have.been.calledWith('pointerover'); + + expect(element.removeEventListener).to.have.been.calledWith('touchstart'); + expect(element.removeEventListener).to.have.been.calledWith('touchcancel'); + expect(element.removeEventListener).to.have.been.calledWith('touchend'); + expect(element.removeEventListener).to.have.been.calledWith('touchmove'); + }); + + it('should add and remove pointer events to element three times when touch events are not supported', function () + { + const manager = new PIXI.interaction.InteractionManager(sinon.stub()); + const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; + + manager.interactionDOMElement = element; + manager.supportsPointerEvents = true; + manager.supportsTouchEvents = false; manager.addEvents(); diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 4d95e15..2420a7d 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -266,6 +266,7 @@ reset() { this.runners.reset.run(); + return this; } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 5e065e2..7cd460c 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -2,6 +2,8 @@ import Program from '../../../shader/Program'; import { BLEND_MODES } from '../../../const'; import settings from '../../../settings'; +import { uid } from '../../../utils'; +import extractUniformsFromSrc from './extractUniformsFromSrc'; // let math = require('../../../math'); /** @@ -49,12 +51,12 @@ this.glShaders = {}; // used for cacheing.. sure there is a better way! - if (!SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) + if (!Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) { - SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); + Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); } - this.glShaderKey = SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; + this.glShaderKey = Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; /** * The padding of the filter. Some filters require extra space to breath such as a blur. @@ -185,3 +187,12 @@ ].join('\n'); } } + +/** + * Used for caching shader IDs + * + * @static + * @private + */ +Filter.SOURCE_KEY_MAP = {}; + diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js index 5c4ca3f..3843739 100644 --- a/src/core/renderers/webgl/systems/StencilSystem.js +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -125,6 +125,6 @@ { super.destroy(this); - this.stencilMaskStack.stencilStack = null; + this.stencilMaskStack = null; } } diff --git a/src/deprecation.js b/src/deprecation.js index d1b689e..209910a 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -601,6 +601,21 @@ return this.generateCanvasTexture(scaleMode, resolution); }; + /** + * @method + * @name PIXI.GroupD8.isSwapWidthHeight + * @see PIXI.GroupD8.isVertical + * @param {number} rotation - The number to check. + * @returns {boolean} Whether or not the direction is vertical + * @deprecated since version 4.6.0 + */ + core.GroupD8.isSwapWidthHeight = function isSwapWidthHeight(rotation) + { + warn('GroupD8.isSwapWidthHeight was renamed to GroupD8.isVertical'); + + return core.GroupD8.isVertical(rotation); + }; + core.RenderTexture.prototype.render = function render(displayObject, matrix, clear, updateTransform) { this.legacyRenderer.render(displayObject, this, clear, matrix, !updateTransform); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 0592b54..96d9f44 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -1,5 +1,6 @@ import * as core from '../core'; import ObservablePoint from '../core/math/ObservablePoint'; +import { getResolutionOfUrl } from '../core/utils'; import settings from '../core/settings'; /** @@ -510,7 +511,8 @@ const data = {}; const info = xml.getElementsByTagName('info')[0]; const common = xml.getElementsByTagName('common')[0]; - const res = texture.baseTexture.resolution || settings.RESOLUTION; + const fileName = xml.getElementsByTagName('page')[0].getAttribute('file'); + const res = getResolutionOfUrl(fileName, settings.RESOLUTION); data.font = info.getAttribute('face'); data.size = parseInt(info.getAttribute('size'), 10); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js index 3f84d6a..136640e 100644 --- a/test/interaction/InteractionManager.js +++ b/test/interaction/InteractionManager.js @@ -279,13 +279,48 @@ removeSpy.restore(); }); - it('should add and remove pointer events to element', function () + it('should add and remove pointer events to element seven times when touch events are supported', function () { const manager = new PIXI.interaction.InteractionManager(sinon.stub()); const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; manager.interactionDOMElement = element; manager.supportsPointerEvents = true; + manager.supportsTouchEvents = true; + + manager.addEvents(); + + expect(element.addEventListener).to.have.been.callCount(7); + expect(element.addEventListener).to.have.been.calledWith('pointerdown'); + expect(element.addEventListener).to.have.been.calledWith('pointerleave'); + expect(element.addEventListener).to.have.been.calledWith('pointerover'); + + expect(element.addEventListener).to.have.been.calledWith('touchstart'); + expect(element.addEventListener).to.have.been.calledWith('touchcancel'); + expect(element.addEventListener).to.have.been.calledWith('touchend'); + expect(element.addEventListener).to.have.been.calledWith('touchmove'); + + manager.removeEvents(); + + expect(element.removeEventListener).to.have.been.callCount(7); + expect(element.removeEventListener).to.have.been.calledWith('pointerdown'); + expect(element.removeEventListener).to.have.been.calledWith('pointerleave'); + expect(element.removeEventListener).to.have.been.calledWith('pointerover'); + + expect(element.removeEventListener).to.have.been.calledWith('touchstart'); + expect(element.removeEventListener).to.have.been.calledWith('touchcancel'); + expect(element.removeEventListener).to.have.been.calledWith('touchend'); + expect(element.removeEventListener).to.have.been.calledWith('touchmove'); + }); + + it('should add and remove pointer events to element three times when touch events are not supported', function () + { + const manager = new PIXI.interaction.InteractionManager(sinon.stub()); + const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; + + manager.interactionDOMElement = element; + manager.supportsPointerEvents = true; + manager.supportsTouchEvents = false; manager.addEvents(); diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js index 7c77343..70703fc 100644 --- a/test/loaders/bitmapFontParser.js +++ b/test/loaders/bitmapFontParser.js @@ -1,7 +1,81 @@ 'use strict'; +const path = require('path'); +const fs = require('fs'); + describe('PIXI.loaders.bitmapFontParser', function () { + afterEach(function () + { + for (var font in PIXI.extras.BitmapText.fonts) + { + delete PIXI.extras.BitmapText.fonts[font]; + } + for (var baseTexture in PIXI.utils.BaseTextureCache) + { + delete PIXI.utils.BaseTextureCache[baseTexture]; + } + for (var texture in PIXI.utils.TextureCache) + { + delete PIXI.utils.TextureCache[texture]; + } + }); + + before(function (done) + { + const resolveURL = (url) => path.resolve(this.resources, url); + + this.resources = path.join(__dirname, 'resources'); + this.fontXML = null; + this.fontScaledXML = null; + this.fontImage = null; + this.fontScaledImage = null; + this.atlasImage = null; + this.atlasScaledImage = null; + this.atlasJSON = require(resolveURL('atlas.json')); // eslint-disable-line global-require + this.atlasScaledJSON = require(resolveURL('atlas@0.5x.json')); // eslint-disable-line global-require + + const loadXML = (url) => new Promise((resolve) => + fs.readFile(resolveURL(url), 'utf8', (err, data) => + { + expect(err).to.be.null; + resolve((new window.DOMParser()).parseFromString(data, 'text/xml')); + })); + + const loadImage = (url) => new Promise((resolve) => + { + const image = new Image(); + + image.onload = () => resolve(image); + image.src = resolveURL(url); + }); + + Promise.all([ + loadXML('font.fnt'), + loadXML('font@0.5x.fnt'), + loadImage('font.png'), + loadImage('font@0.5x.png'), + loadImage('atlas.png'), + loadImage('atlas@0.5x.png'), + ]).then(([ + fontXML, + fontScaledXML, + fontImage, + fontScaledImage, + atlasImage, + atlasScaledImage, + ]) => + { + this.fontXML = fontXML; + this.fontScaledXML = fontScaledXML; + this.fontImage = fontImage; + this.fontScaledImage = fontScaledImage; + this.atlasImage = atlasImage; + this.atlasScaledImage = atlasScaledImage; + done(); + }); + }); + it('should exist and return a function', function () { expect(PIXI.loaders.bitmapFontParser).to.be.a('function'); @@ -33,6 +107,206 @@ // TODO: Test the texture cache code path. // TODO: Test the loading texture code path. // TODO: Test data-url code paths. + + it('should properly register bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontImage, null, 1)); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontImage); + expect(charA.texture.frame.x).to.equal(2); + expect(charA.texture.frame.y).to.equal(2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontImage); + expect(charB.texture.frame.x).to.equal(2); + expect(charB.texture.frame.y).to.equal(24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontImage); + expect(charC.texture.frame.x).to.equal(23); + expect(charC.texture.frame.y).to.equal(2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontImage); + expect(charD.texture.frame.x).to.equal(19); + expect(charD.texture.frame.y).to.equal(24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register SCALED bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontScaledImage, null, 0.5)); + const font = PIXI.extras.BitmapText.registerFont(this.fontScaledXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charA.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.width).to.equal(38); // 19 / 0.5 + expect(charA.texture.frame.height).to.equal(40); // 20 / 0.5 + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charB.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charB.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charB.texture.frame.width).to.equal(30); // 15 / 0.5 + expect(charB.texture.frame.height).to.equal(40); // 20 / 0.5 + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charC.texture.frame.x).to.equal(46); // 23 / 0.5 + expect(charC.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charC.texture.frame.width).to.equal(36); // 18 / 0.5 + expect(charC.texture.frame.height).to.equal(40); // 20 / 0.5 + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charD.texture.frame.x).to.equal(38); // 19 / 0.5 + expect(charD.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charD.texture.frame.width).to.equal(34); // 17 / 0.5 + expect(charD.texture.frame.height).to.equal(40); // 20 / 0.5 + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register bitmap font NESTED into spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); + + it('should properly register bitmap font NESTED into SCALED spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasScaledImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasScaledJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); }); describe('PIXI.loaders.parseBitmapFontData', function () diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 4d95e15..2420a7d 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -266,6 +266,7 @@ reset() { this.runners.reset.run(); + return this; } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 5e065e2..7cd460c 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -2,6 +2,8 @@ import Program from '../../../shader/Program'; import { BLEND_MODES } from '../../../const'; import settings from '../../../settings'; +import { uid } from '../../../utils'; +import extractUniformsFromSrc from './extractUniformsFromSrc'; // let math = require('../../../math'); /** @@ -49,12 +51,12 @@ this.glShaders = {}; // used for cacheing.. sure there is a better way! - if (!SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) + if (!Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) { - SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); + Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); } - this.glShaderKey = SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; + this.glShaderKey = Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; /** * The padding of the filter. Some filters require extra space to breath such as a blur. @@ -185,3 +187,12 @@ ].join('\n'); } } + +/** + * Used for caching shader IDs + * + * @static + * @private + */ +Filter.SOURCE_KEY_MAP = {}; + diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js index 5c4ca3f..3843739 100644 --- a/src/core/renderers/webgl/systems/StencilSystem.js +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -125,6 +125,6 @@ { super.destroy(this); - this.stencilMaskStack.stencilStack = null; + this.stencilMaskStack = null; } } diff --git a/src/deprecation.js b/src/deprecation.js index d1b689e..209910a 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -601,6 +601,21 @@ return this.generateCanvasTexture(scaleMode, resolution); }; + /** + * @method + * @name PIXI.GroupD8.isSwapWidthHeight + * @see PIXI.GroupD8.isVertical + * @param {number} rotation - The number to check. + * @returns {boolean} Whether or not the direction is vertical + * @deprecated since version 4.6.0 + */ + core.GroupD8.isSwapWidthHeight = function isSwapWidthHeight(rotation) + { + warn('GroupD8.isSwapWidthHeight was renamed to GroupD8.isVertical'); + + return core.GroupD8.isVertical(rotation); + }; + core.RenderTexture.prototype.render = function render(displayObject, matrix, clear, updateTransform) { this.legacyRenderer.render(displayObject, this, clear, matrix, !updateTransform); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 0592b54..96d9f44 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -1,5 +1,6 @@ import * as core from '../core'; import ObservablePoint from '../core/math/ObservablePoint'; +import { getResolutionOfUrl } from '../core/utils'; import settings from '../core/settings'; /** @@ -510,7 +511,8 @@ const data = {}; const info = xml.getElementsByTagName('info')[0]; const common = xml.getElementsByTagName('common')[0]; - const res = texture.baseTexture.resolution || settings.RESOLUTION; + const fileName = xml.getElementsByTagName('page')[0].getAttribute('file'); + const res = getResolutionOfUrl(fileName, settings.RESOLUTION); data.font = info.getAttribute('face'); data.size = parseInt(info.getAttribute('size'), 10); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js index 3f84d6a..136640e 100644 --- a/test/interaction/InteractionManager.js +++ b/test/interaction/InteractionManager.js @@ -279,13 +279,48 @@ removeSpy.restore(); }); - it('should add and remove pointer events to element', function () + it('should add and remove pointer events to element seven times when touch events are supported', function () { const manager = new PIXI.interaction.InteractionManager(sinon.stub()); const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; manager.interactionDOMElement = element; manager.supportsPointerEvents = true; + manager.supportsTouchEvents = true; + + manager.addEvents(); + + expect(element.addEventListener).to.have.been.callCount(7); + expect(element.addEventListener).to.have.been.calledWith('pointerdown'); + expect(element.addEventListener).to.have.been.calledWith('pointerleave'); + expect(element.addEventListener).to.have.been.calledWith('pointerover'); + + expect(element.addEventListener).to.have.been.calledWith('touchstart'); + expect(element.addEventListener).to.have.been.calledWith('touchcancel'); + expect(element.addEventListener).to.have.been.calledWith('touchend'); + expect(element.addEventListener).to.have.been.calledWith('touchmove'); + + manager.removeEvents(); + + expect(element.removeEventListener).to.have.been.callCount(7); + expect(element.removeEventListener).to.have.been.calledWith('pointerdown'); + expect(element.removeEventListener).to.have.been.calledWith('pointerleave'); + expect(element.removeEventListener).to.have.been.calledWith('pointerover'); + + expect(element.removeEventListener).to.have.been.calledWith('touchstart'); + expect(element.removeEventListener).to.have.been.calledWith('touchcancel'); + expect(element.removeEventListener).to.have.been.calledWith('touchend'); + expect(element.removeEventListener).to.have.been.calledWith('touchmove'); + }); + + it('should add and remove pointer events to element three times when touch events are not supported', function () + { + const manager = new PIXI.interaction.InteractionManager(sinon.stub()); + const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; + + manager.interactionDOMElement = element; + manager.supportsPointerEvents = true; + manager.supportsTouchEvents = false; manager.addEvents(); diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js index 7c77343..70703fc 100644 --- a/test/loaders/bitmapFontParser.js +++ b/test/loaders/bitmapFontParser.js @@ -1,7 +1,81 @@ 'use strict'; +const path = require('path'); +const fs = require('fs'); + describe('PIXI.loaders.bitmapFontParser', function () { + afterEach(function () + { + for (var font in PIXI.extras.BitmapText.fonts) + { + delete PIXI.extras.BitmapText.fonts[font]; + } + for (var baseTexture in PIXI.utils.BaseTextureCache) + { + delete PIXI.utils.BaseTextureCache[baseTexture]; + } + for (var texture in PIXI.utils.TextureCache) + { + delete PIXI.utils.TextureCache[texture]; + } + }); + + before(function (done) + { + const resolveURL = (url) => path.resolve(this.resources, url); + + this.resources = path.join(__dirname, 'resources'); + this.fontXML = null; + this.fontScaledXML = null; + this.fontImage = null; + this.fontScaledImage = null; + this.atlasImage = null; + this.atlasScaledImage = null; + this.atlasJSON = require(resolveURL('atlas.json')); // eslint-disable-line global-require + this.atlasScaledJSON = require(resolveURL('atlas@0.5x.json')); // eslint-disable-line global-require + + const loadXML = (url) => new Promise((resolve) => + fs.readFile(resolveURL(url), 'utf8', (err, data) => + { + expect(err).to.be.null; + resolve((new window.DOMParser()).parseFromString(data, 'text/xml')); + })); + + const loadImage = (url) => new Promise((resolve) => + { + const image = new Image(); + + image.onload = () => resolve(image); + image.src = resolveURL(url); + }); + + Promise.all([ + loadXML('font.fnt'), + loadXML('font@0.5x.fnt'), + loadImage('font.png'), + loadImage('font@0.5x.png'), + loadImage('atlas.png'), + loadImage('atlas@0.5x.png'), + ]).then(([ + fontXML, + fontScaledXML, + fontImage, + fontScaledImage, + atlasImage, + atlasScaledImage, + ]) => + { + this.fontXML = fontXML; + this.fontScaledXML = fontScaledXML; + this.fontImage = fontImage; + this.fontScaledImage = fontScaledImage; + this.atlasImage = atlasImage; + this.atlasScaledImage = atlasScaledImage; + done(); + }); + }); + it('should exist and return a function', function () { expect(PIXI.loaders.bitmapFontParser).to.be.a('function'); @@ -33,6 +107,206 @@ // TODO: Test the texture cache code path. // TODO: Test the loading texture code path. // TODO: Test data-url code paths. + + it('should properly register bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontImage, null, 1)); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontImage); + expect(charA.texture.frame.x).to.equal(2); + expect(charA.texture.frame.y).to.equal(2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontImage); + expect(charB.texture.frame.x).to.equal(2); + expect(charB.texture.frame.y).to.equal(24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontImage); + expect(charC.texture.frame.x).to.equal(23); + expect(charC.texture.frame.y).to.equal(2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontImage); + expect(charD.texture.frame.x).to.equal(19); + expect(charD.texture.frame.y).to.equal(24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register SCALED bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontScaledImage, null, 0.5)); + const font = PIXI.extras.BitmapText.registerFont(this.fontScaledXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charA.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.width).to.equal(38); // 19 / 0.5 + expect(charA.texture.frame.height).to.equal(40); // 20 / 0.5 + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charB.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charB.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charB.texture.frame.width).to.equal(30); // 15 / 0.5 + expect(charB.texture.frame.height).to.equal(40); // 20 / 0.5 + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charC.texture.frame.x).to.equal(46); // 23 / 0.5 + expect(charC.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charC.texture.frame.width).to.equal(36); // 18 / 0.5 + expect(charC.texture.frame.height).to.equal(40); // 20 / 0.5 + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charD.texture.frame.x).to.equal(38); // 19 / 0.5 + expect(charD.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charD.texture.frame.width).to.equal(34); // 17 / 0.5 + expect(charD.texture.frame.height).to.equal(40); // 20 / 0.5 + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register bitmap font NESTED into spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); + + it('should properly register bitmap font NESTED into SCALED spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasScaledImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasScaledJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); }); describe('PIXI.loaders.parseBitmapFontData', function () diff --git a/test/loaders/resources/atlas.json b/test/loaders/resources/atlas.json new file mode 100644 index 0000000..86e65a5 --- /dev/null +++ b/test/loaders/resources/atlas.json @@ -0,0 +1,25 @@ +{ + "meta": { + "image": "atlas.png", + "size": {"w":256,"h":256}, + "scale": "1" + }, + "frames": { + "resources/test.png": + { + "frame": {"x":2,"y":2,"w":152,"h":188}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":74,"y":36,"w":152,"h":188}, + "sourceSize": {"w":300,"h":225} + }, + "resources/font.png": + { + "frame": {"x":158,"y":2,"w":40,"h":42}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":40,"h":42}, + "sourceSize": {"w":43,"h":46} + } + } +} \ No newline at end of file diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 4d95e15..2420a7d 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -266,6 +266,7 @@ reset() { this.runners.reset.run(); + return this; } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 5e065e2..7cd460c 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -2,6 +2,8 @@ import Program from '../../../shader/Program'; import { BLEND_MODES } from '../../../const'; import settings from '../../../settings'; +import { uid } from '../../../utils'; +import extractUniformsFromSrc from './extractUniformsFromSrc'; // let math = require('../../../math'); /** @@ -49,12 +51,12 @@ this.glShaders = {}; // used for cacheing.. sure there is a better way! - if (!SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) + if (!Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) { - SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); + Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); } - this.glShaderKey = SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; + this.glShaderKey = Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; /** * The padding of the filter. Some filters require extra space to breath such as a blur. @@ -185,3 +187,12 @@ ].join('\n'); } } + +/** + * Used for caching shader IDs + * + * @static + * @private + */ +Filter.SOURCE_KEY_MAP = {}; + diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js index 5c4ca3f..3843739 100644 --- a/src/core/renderers/webgl/systems/StencilSystem.js +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -125,6 +125,6 @@ { super.destroy(this); - this.stencilMaskStack.stencilStack = null; + this.stencilMaskStack = null; } } diff --git a/src/deprecation.js b/src/deprecation.js index d1b689e..209910a 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -601,6 +601,21 @@ return this.generateCanvasTexture(scaleMode, resolution); }; + /** + * @method + * @name PIXI.GroupD8.isSwapWidthHeight + * @see PIXI.GroupD8.isVertical + * @param {number} rotation - The number to check. + * @returns {boolean} Whether or not the direction is vertical + * @deprecated since version 4.6.0 + */ + core.GroupD8.isSwapWidthHeight = function isSwapWidthHeight(rotation) + { + warn('GroupD8.isSwapWidthHeight was renamed to GroupD8.isVertical'); + + return core.GroupD8.isVertical(rotation); + }; + core.RenderTexture.prototype.render = function render(displayObject, matrix, clear, updateTransform) { this.legacyRenderer.render(displayObject, this, clear, matrix, !updateTransform); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 0592b54..96d9f44 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -1,5 +1,6 @@ import * as core from '../core'; import ObservablePoint from '../core/math/ObservablePoint'; +import { getResolutionOfUrl } from '../core/utils'; import settings from '../core/settings'; /** @@ -510,7 +511,8 @@ const data = {}; const info = xml.getElementsByTagName('info')[0]; const common = xml.getElementsByTagName('common')[0]; - const res = texture.baseTexture.resolution || settings.RESOLUTION; + const fileName = xml.getElementsByTagName('page')[0].getAttribute('file'); + const res = getResolutionOfUrl(fileName, settings.RESOLUTION); data.font = info.getAttribute('face'); data.size = parseInt(info.getAttribute('size'), 10); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js index 3f84d6a..136640e 100644 --- a/test/interaction/InteractionManager.js +++ b/test/interaction/InteractionManager.js @@ -279,13 +279,48 @@ removeSpy.restore(); }); - it('should add and remove pointer events to element', function () + it('should add and remove pointer events to element seven times when touch events are supported', function () { const manager = new PIXI.interaction.InteractionManager(sinon.stub()); const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; manager.interactionDOMElement = element; manager.supportsPointerEvents = true; + manager.supportsTouchEvents = true; + + manager.addEvents(); + + expect(element.addEventListener).to.have.been.callCount(7); + expect(element.addEventListener).to.have.been.calledWith('pointerdown'); + expect(element.addEventListener).to.have.been.calledWith('pointerleave'); + expect(element.addEventListener).to.have.been.calledWith('pointerover'); + + expect(element.addEventListener).to.have.been.calledWith('touchstart'); + expect(element.addEventListener).to.have.been.calledWith('touchcancel'); + expect(element.addEventListener).to.have.been.calledWith('touchend'); + expect(element.addEventListener).to.have.been.calledWith('touchmove'); + + manager.removeEvents(); + + expect(element.removeEventListener).to.have.been.callCount(7); + expect(element.removeEventListener).to.have.been.calledWith('pointerdown'); + expect(element.removeEventListener).to.have.been.calledWith('pointerleave'); + expect(element.removeEventListener).to.have.been.calledWith('pointerover'); + + expect(element.removeEventListener).to.have.been.calledWith('touchstart'); + expect(element.removeEventListener).to.have.been.calledWith('touchcancel'); + expect(element.removeEventListener).to.have.been.calledWith('touchend'); + expect(element.removeEventListener).to.have.been.calledWith('touchmove'); + }); + + it('should add and remove pointer events to element three times when touch events are not supported', function () + { + const manager = new PIXI.interaction.InteractionManager(sinon.stub()); + const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; + + manager.interactionDOMElement = element; + manager.supportsPointerEvents = true; + manager.supportsTouchEvents = false; manager.addEvents(); diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js index 7c77343..70703fc 100644 --- a/test/loaders/bitmapFontParser.js +++ b/test/loaders/bitmapFontParser.js @@ -1,7 +1,81 @@ 'use strict'; +const path = require('path'); +const fs = require('fs'); + describe('PIXI.loaders.bitmapFontParser', function () { + afterEach(function () + { + for (var font in PIXI.extras.BitmapText.fonts) + { + delete PIXI.extras.BitmapText.fonts[font]; + } + for (var baseTexture in PIXI.utils.BaseTextureCache) + { + delete PIXI.utils.BaseTextureCache[baseTexture]; + } + for (var texture in PIXI.utils.TextureCache) + { + delete PIXI.utils.TextureCache[texture]; + } + }); + + before(function (done) + { + const resolveURL = (url) => path.resolve(this.resources, url); + + this.resources = path.join(__dirname, 'resources'); + this.fontXML = null; + this.fontScaledXML = null; + this.fontImage = null; + this.fontScaledImage = null; + this.atlasImage = null; + this.atlasScaledImage = null; + this.atlasJSON = require(resolveURL('atlas.json')); // eslint-disable-line global-require + this.atlasScaledJSON = require(resolveURL('atlas@0.5x.json')); // eslint-disable-line global-require + + const loadXML = (url) => new Promise((resolve) => + fs.readFile(resolveURL(url), 'utf8', (err, data) => + { + expect(err).to.be.null; + resolve((new window.DOMParser()).parseFromString(data, 'text/xml')); + })); + + const loadImage = (url) => new Promise((resolve) => + { + const image = new Image(); + + image.onload = () => resolve(image); + image.src = resolveURL(url); + }); + + Promise.all([ + loadXML('font.fnt'), + loadXML('font@0.5x.fnt'), + loadImage('font.png'), + loadImage('font@0.5x.png'), + loadImage('atlas.png'), + loadImage('atlas@0.5x.png'), + ]).then(([ + fontXML, + fontScaledXML, + fontImage, + fontScaledImage, + atlasImage, + atlasScaledImage, + ]) => + { + this.fontXML = fontXML; + this.fontScaledXML = fontScaledXML; + this.fontImage = fontImage; + this.fontScaledImage = fontScaledImage; + this.atlasImage = atlasImage; + this.atlasScaledImage = atlasScaledImage; + done(); + }); + }); + it('should exist and return a function', function () { expect(PIXI.loaders.bitmapFontParser).to.be.a('function'); @@ -33,6 +107,206 @@ // TODO: Test the texture cache code path. // TODO: Test the loading texture code path. // TODO: Test data-url code paths. + + it('should properly register bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontImage, null, 1)); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontImage); + expect(charA.texture.frame.x).to.equal(2); + expect(charA.texture.frame.y).to.equal(2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontImage); + expect(charB.texture.frame.x).to.equal(2); + expect(charB.texture.frame.y).to.equal(24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontImage); + expect(charC.texture.frame.x).to.equal(23); + expect(charC.texture.frame.y).to.equal(2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontImage); + expect(charD.texture.frame.x).to.equal(19); + expect(charD.texture.frame.y).to.equal(24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register SCALED bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontScaledImage, null, 0.5)); + const font = PIXI.extras.BitmapText.registerFont(this.fontScaledXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charA.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.width).to.equal(38); // 19 / 0.5 + expect(charA.texture.frame.height).to.equal(40); // 20 / 0.5 + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charB.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charB.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charB.texture.frame.width).to.equal(30); // 15 / 0.5 + expect(charB.texture.frame.height).to.equal(40); // 20 / 0.5 + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charC.texture.frame.x).to.equal(46); // 23 / 0.5 + expect(charC.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charC.texture.frame.width).to.equal(36); // 18 / 0.5 + expect(charC.texture.frame.height).to.equal(40); // 20 / 0.5 + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charD.texture.frame.x).to.equal(38); // 19 / 0.5 + expect(charD.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charD.texture.frame.width).to.equal(34); // 17 / 0.5 + expect(charD.texture.frame.height).to.equal(40); // 20 / 0.5 + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register bitmap font NESTED into spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); + + it('should properly register bitmap font NESTED into SCALED spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasScaledImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasScaledJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); }); describe('PIXI.loaders.parseBitmapFontData', function () diff --git a/test/loaders/resources/atlas.json b/test/loaders/resources/atlas.json new file mode 100644 index 0000000..86e65a5 --- /dev/null +++ b/test/loaders/resources/atlas.json @@ -0,0 +1,25 @@ +{ + "meta": { + "image": "atlas.png", + "size": {"w":256,"h":256}, + "scale": "1" + }, + "frames": { + "resources/test.png": + { + "frame": {"x":2,"y":2,"w":152,"h":188}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":74,"y":36,"w":152,"h":188}, + "sourceSize": {"w":300,"h":225} + }, + "resources/font.png": + { + "frame": {"x":158,"y":2,"w":40,"h":42}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":40,"h":42}, + "sourceSize": {"w":43,"h":46} + } + } +} \ No newline at end of file diff --git a/test/loaders/resources/atlas.png b/test/loaders/resources/atlas.png new file mode 100644 index 0000000..d5e7892 --- /dev/null +++ b/test/loaders/resources/atlas.png Binary files differ diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 4d95e15..2420a7d 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -266,6 +266,7 @@ reset() { this.runners.reset.run(); + return this; } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 5e065e2..7cd460c 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -2,6 +2,8 @@ import Program from '../../../shader/Program'; import { BLEND_MODES } from '../../../const'; import settings from '../../../settings'; +import { uid } from '../../../utils'; +import extractUniformsFromSrc from './extractUniformsFromSrc'; // let math = require('../../../math'); /** @@ -49,12 +51,12 @@ this.glShaders = {}; // used for cacheing.. sure there is a better way! - if (!SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) + if (!Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) { - SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); + Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); } - this.glShaderKey = SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; + this.glShaderKey = Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; /** * The padding of the filter. Some filters require extra space to breath such as a blur. @@ -185,3 +187,12 @@ ].join('\n'); } } + +/** + * Used for caching shader IDs + * + * @static + * @private + */ +Filter.SOURCE_KEY_MAP = {}; + diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js index 5c4ca3f..3843739 100644 --- a/src/core/renderers/webgl/systems/StencilSystem.js +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -125,6 +125,6 @@ { super.destroy(this); - this.stencilMaskStack.stencilStack = null; + this.stencilMaskStack = null; } } diff --git a/src/deprecation.js b/src/deprecation.js index d1b689e..209910a 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -601,6 +601,21 @@ return this.generateCanvasTexture(scaleMode, resolution); }; + /** + * @method + * @name PIXI.GroupD8.isSwapWidthHeight + * @see PIXI.GroupD8.isVertical + * @param {number} rotation - The number to check. + * @returns {boolean} Whether or not the direction is vertical + * @deprecated since version 4.6.0 + */ + core.GroupD8.isSwapWidthHeight = function isSwapWidthHeight(rotation) + { + warn('GroupD8.isSwapWidthHeight was renamed to GroupD8.isVertical'); + + return core.GroupD8.isVertical(rotation); + }; + core.RenderTexture.prototype.render = function render(displayObject, matrix, clear, updateTransform) { this.legacyRenderer.render(displayObject, this, clear, matrix, !updateTransform); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 0592b54..96d9f44 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -1,5 +1,6 @@ import * as core from '../core'; import ObservablePoint from '../core/math/ObservablePoint'; +import { getResolutionOfUrl } from '../core/utils'; import settings from '../core/settings'; /** @@ -510,7 +511,8 @@ const data = {}; const info = xml.getElementsByTagName('info')[0]; const common = xml.getElementsByTagName('common')[0]; - const res = texture.baseTexture.resolution || settings.RESOLUTION; + const fileName = xml.getElementsByTagName('page')[0].getAttribute('file'); + const res = getResolutionOfUrl(fileName, settings.RESOLUTION); data.font = info.getAttribute('face'); data.size = parseInt(info.getAttribute('size'), 10); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js index 3f84d6a..136640e 100644 --- a/test/interaction/InteractionManager.js +++ b/test/interaction/InteractionManager.js @@ -279,13 +279,48 @@ removeSpy.restore(); }); - it('should add and remove pointer events to element', function () + it('should add and remove pointer events to element seven times when touch events are supported', function () { const manager = new PIXI.interaction.InteractionManager(sinon.stub()); const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; manager.interactionDOMElement = element; manager.supportsPointerEvents = true; + manager.supportsTouchEvents = true; + + manager.addEvents(); + + expect(element.addEventListener).to.have.been.callCount(7); + expect(element.addEventListener).to.have.been.calledWith('pointerdown'); + expect(element.addEventListener).to.have.been.calledWith('pointerleave'); + expect(element.addEventListener).to.have.been.calledWith('pointerover'); + + expect(element.addEventListener).to.have.been.calledWith('touchstart'); + expect(element.addEventListener).to.have.been.calledWith('touchcancel'); + expect(element.addEventListener).to.have.been.calledWith('touchend'); + expect(element.addEventListener).to.have.been.calledWith('touchmove'); + + manager.removeEvents(); + + expect(element.removeEventListener).to.have.been.callCount(7); + expect(element.removeEventListener).to.have.been.calledWith('pointerdown'); + expect(element.removeEventListener).to.have.been.calledWith('pointerleave'); + expect(element.removeEventListener).to.have.been.calledWith('pointerover'); + + expect(element.removeEventListener).to.have.been.calledWith('touchstart'); + expect(element.removeEventListener).to.have.been.calledWith('touchcancel'); + expect(element.removeEventListener).to.have.been.calledWith('touchend'); + expect(element.removeEventListener).to.have.been.calledWith('touchmove'); + }); + + it('should add and remove pointer events to element three times when touch events are not supported', function () + { + const manager = new PIXI.interaction.InteractionManager(sinon.stub()); + const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; + + manager.interactionDOMElement = element; + manager.supportsPointerEvents = true; + manager.supportsTouchEvents = false; manager.addEvents(); diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js index 7c77343..70703fc 100644 --- a/test/loaders/bitmapFontParser.js +++ b/test/loaders/bitmapFontParser.js @@ -1,7 +1,81 @@ 'use strict'; +const path = require('path'); +const fs = require('fs'); + describe('PIXI.loaders.bitmapFontParser', function () { + afterEach(function () + { + for (var font in PIXI.extras.BitmapText.fonts) + { + delete PIXI.extras.BitmapText.fonts[font]; + } + for (var baseTexture in PIXI.utils.BaseTextureCache) + { + delete PIXI.utils.BaseTextureCache[baseTexture]; + } + for (var texture in PIXI.utils.TextureCache) + { + delete PIXI.utils.TextureCache[texture]; + } + }); + + before(function (done) + { + const resolveURL = (url) => path.resolve(this.resources, url); + + this.resources = path.join(__dirname, 'resources'); + this.fontXML = null; + this.fontScaledXML = null; + this.fontImage = null; + this.fontScaledImage = null; + this.atlasImage = null; + this.atlasScaledImage = null; + this.atlasJSON = require(resolveURL('atlas.json')); // eslint-disable-line global-require + this.atlasScaledJSON = require(resolveURL('atlas@0.5x.json')); // eslint-disable-line global-require + + const loadXML = (url) => new Promise((resolve) => + fs.readFile(resolveURL(url), 'utf8', (err, data) => + { + expect(err).to.be.null; + resolve((new window.DOMParser()).parseFromString(data, 'text/xml')); + })); + + const loadImage = (url) => new Promise((resolve) => + { + const image = new Image(); + + image.onload = () => resolve(image); + image.src = resolveURL(url); + }); + + Promise.all([ + loadXML('font.fnt'), + loadXML('font@0.5x.fnt'), + loadImage('font.png'), + loadImage('font@0.5x.png'), + loadImage('atlas.png'), + loadImage('atlas@0.5x.png'), + ]).then(([ + fontXML, + fontScaledXML, + fontImage, + fontScaledImage, + atlasImage, + atlasScaledImage, + ]) => + { + this.fontXML = fontXML; + this.fontScaledXML = fontScaledXML; + this.fontImage = fontImage; + this.fontScaledImage = fontScaledImage; + this.atlasImage = atlasImage; + this.atlasScaledImage = atlasScaledImage; + done(); + }); + }); + it('should exist and return a function', function () { expect(PIXI.loaders.bitmapFontParser).to.be.a('function'); @@ -33,6 +107,206 @@ // TODO: Test the texture cache code path. // TODO: Test the loading texture code path. // TODO: Test data-url code paths. + + it('should properly register bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontImage, null, 1)); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontImage); + expect(charA.texture.frame.x).to.equal(2); + expect(charA.texture.frame.y).to.equal(2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontImage); + expect(charB.texture.frame.x).to.equal(2); + expect(charB.texture.frame.y).to.equal(24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontImage); + expect(charC.texture.frame.x).to.equal(23); + expect(charC.texture.frame.y).to.equal(2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontImage); + expect(charD.texture.frame.x).to.equal(19); + expect(charD.texture.frame.y).to.equal(24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register SCALED bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontScaledImage, null, 0.5)); + const font = PIXI.extras.BitmapText.registerFont(this.fontScaledXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charA.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.width).to.equal(38); // 19 / 0.5 + expect(charA.texture.frame.height).to.equal(40); // 20 / 0.5 + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charB.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charB.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charB.texture.frame.width).to.equal(30); // 15 / 0.5 + expect(charB.texture.frame.height).to.equal(40); // 20 / 0.5 + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charC.texture.frame.x).to.equal(46); // 23 / 0.5 + expect(charC.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charC.texture.frame.width).to.equal(36); // 18 / 0.5 + expect(charC.texture.frame.height).to.equal(40); // 20 / 0.5 + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charD.texture.frame.x).to.equal(38); // 19 / 0.5 + expect(charD.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charD.texture.frame.width).to.equal(34); // 17 / 0.5 + expect(charD.texture.frame.height).to.equal(40); // 20 / 0.5 + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register bitmap font NESTED into spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); + + it('should properly register bitmap font NESTED into SCALED spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasScaledImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasScaledJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); }); describe('PIXI.loaders.parseBitmapFontData', function () diff --git a/test/loaders/resources/atlas.json b/test/loaders/resources/atlas.json new file mode 100644 index 0000000..86e65a5 --- /dev/null +++ b/test/loaders/resources/atlas.json @@ -0,0 +1,25 @@ +{ + "meta": { + "image": "atlas.png", + "size": {"w":256,"h":256}, + "scale": "1" + }, + "frames": { + "resources/test.png": + { + "frame": {"x":2,"y":2,"w":152,"h":188}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":74,"y":36,"w":152,"h":188}, + "sourceSize": {"w":300,"h":225} + }, + "resources/font.png": + { + "frame": {"x":158,"y":2,"w":40,"h":42}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":40,"h":42}, + "sourceSize": {"w":43,"h":46} + } + } +} \ No newline at end of file diff --git a/test/loaders/resources/atlas.png b/test/loaders/resources/atlas.png new file mode 100644 index 0000000..d5e7892 --- /dev/null +++ b/test/loaders/resources/atlas.png Binary files differ diff --git a/test/loaders/resources/atlas@0.5x.json b/test/loaders/resources/atlas@0.5x.json new file mode 100644 index 0000000..ae990a1 --- /dev/null +++ b/test/loaders/resources/atlas@0.5x.json @@ -0,0 +1,25 @@ +{ + "meta": { + "image": "atlas@0.5x.png", + "size": {"w":256,"h":256}, + "scale": "1" + }, + "frames": { + "resources/test.png": + { + "frame": {"x":2,"y":2,"w":152,"h":188}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":74,"y":36,"w":152,"h":188}, + "sourceSize": {"w":300,"h":225} + }, + "resources/font.png": + { + "frame": {"x":158,"y":2,"w":40,"h":42}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":40,"h":42}, + "sourceSize": {"w":43,"h":46} + } + } +} \ No newline at end of file diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 4d95e15..2420a7d 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -266,6 +266,7 @@ reset() { this.runners.reset.run(); + return this; } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 5e065e2..7cd460c 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -2,6 +2,8 @@ import Program from '../../../shader/Program'; import { BLEND_MODES } from '../../../const'; import settings from '../../../settings'; +import { uid } from '../../../utils'; +import extractUniformsFromSrc from './extractUniformsFromSrc'; // let math = require('../../../math'); /** @@ -49,12 +51,12 @@ this.glShaders = {}; // used for cacheing.. sure there is a better way! - if (!SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) + if (!Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) { - SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); + Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); } - this.glShaderKey = SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; + this.glShaderKey = Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; /** * The padding of the filter. Some filters require extra space to breath such as a blur. @@ -185,3 +187,12 @@ ].join('\n'); } } + +/** + * Used for caching shader IDs + * + * @static + * @private + */ +Filter.SOURCE_KEY_MAP = {}; + diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js index 5c4ca3f..3843739 100644 --- a/src/core/renderers/webgl/systems/StencilSystem.js +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -125,6 +125,6 @@ { super.destroy(this); - this.stencilMaskStack.stencilStack = null; + this.stencilMaskStack = null; } } diff --git a/src/deprecation.js b/src/deprecation.js index d1b689e..209910a 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -601,6 +601,21 @@ return this.generateCanvasTexture(scaleMode, resolution); }; + /** + * @method + * @name PIXI.GroupD8.isSwapWidthHeight + * @see PIXI.GroupD8.isVertical + * @param {number} rotation - The number to check. + * @returns {boolean} Whether or not the direction is vertical + * @deprecated since version 4.6.0 + */ + core.GroupD8.isSwapWidthHeight = function isSwapWidthHeight(rotation) + { + warn('GroupD8.isSwapWidthHeight was renamed to GroupD8.isVertical'); + + return core.GroupD8.isVertical(rotation); + }; + core.RenderTexture.prototype.render = function render(displayObject, matrix, clear, updateTransform) { this.legacyRenderer.render(displayObject, this, clear, matrix, !updateTransform); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 0592b54..96d9f44 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -1,5 +1,6 @@ import * as core from '../core'; import ObservablePoint from '../core/math/ObservablePoint'; +import { getResolutionOfUrl } from '../core/utils'; import settings from '../core/settings'; /** @@ -510,7 +511,8 @@ const data = {}; const info = xml.getElementsByTagName('info')[0]; const common = xml.getElementsByTagName('common')[0]; - const res = texture.baseTexture.resolution || settings.RESOLUTION; + const fileName = xml.getElementsByTagName('page')[0].getAttribute('file'); + const res = getResolutionOfUrl(fileName, settings.RESOLUTION); data.font = info.getAttribute('face'); data.size = parseInt(info.getAttribute('size'), 10); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js index 3f84d6a..136640e 100644 --- a/test/interaction/InteractionManager.js +++ b/test/interaction/InteractionManager.js @@ -279,13 +279,48 @@ removeSpy.restore(); }); - it('should add and remove pointer events to element', function () + it('should add and remove pointer events to element seven times when touch events are supported', function () { const manager = new PIXI.interaction.InteractionManager(sinon.stub()); const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; manager.interactionDOMElement = element; manager.supportsPointerEvents = true; + manager.supportsTouchEvents = true; + + manager.addEvents(); + + expect(element.addEventListener).to.have.been.callCount(7); + expect(element.addEventListener).to.have.been.calledWith('pointerdown'); + expect(element.addEventListener).to.have.been.calledWith('pointerleave'); + expect(element.addEventListener).to.have.been.calledWith('pointerover'); + + expect(element.addEventListener).to.have.been.calledWith('touchstart'); + expect(element.addEventListener).to.have.been.calledWith('touchcancel'); + expect(element.addEventListener).to.have.been.calledWith('touchend'); + expect(element.addEventListener).to.have.been.calledWith('touchmove'); + + manager.removeEvents(); + + expect(element.removeEventListener).to.have.been.callCount(7); + expect(element.removeEventListener).to.have.been.calledWith('pointerdown'); + expect(element.removeEventListener).to.have.been.calledWith('pointerleave'); + expect(element.removeEventListener).to.have.been.calledWith('pointerover'); + + expect(element.removeEventListener).to.have.been.calledWith('touchstart'); + expect(element.removeEventListener).to.have.been.calledWith('touchcancel'); + expect(element.removeEventListener).to.have.been.calledWith('touchend'); + expect(element.removeEventListener).to.have.been.calledWith('touchmove'); + }); + + it('should add and remove pointer events to element three times when touch events are not supported', function () + { + const manager = new PIXI.interaction.InteractionManager(sinon.stub()); + const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; + + manager.interactionDOMElement = element; + manager.supportsPointerEvents = true; + manager.supportsTouchEvents = false; manager.addEvents(); diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js index 7c77343..70703fc 100644 --- a/test/loaders/bitmapFontParser.js +++ b/test/loaders/bitmapFontParser.js @@ -1,7 +1,81 @@ 'use strict'; +const path = require('path'); +const fs = require('fs'); + describe('PIXI.loaders.bitmapFontParser', function () { + afterEach(function () + { + for (var font in PIXI.extras.BitmapText.fonts) + { + delete PIXI.extras.BitmapText.fonts[font]; + } + for (var baseTexture in PIXI.utils.BaseTextureCache) + { + delete PIXI.utils.BaseTextureCache[baseTexture]; + } + for (var texture in PIXI.utils.TextureCache) + { + delete PIXI.utils.TextureCache[texture]; + } + }); + + before(function (done) + { + const resolveURL = (url) => path.resolve(this.resources, url); + + this.resources = path.join(__dirname, 'resources'); + this.fontXML = null; + this.fontScaledXML = null; + this.fontImage = null; + this.fontScaledImage = null; + this.atlasImage = null; + this.atlasScaledImage = null; + this.atlasJSON = require(resolveURL('atlas.json')); // eslint-disable-line global-require + this.atlasScaledJSON = require(resolveURL('atlas@0.5x.json')); // eslint-disable-line global-require + + const loadXML = (url) => new Promise((resolve) => + fs.readFile(resolveURL(url), 'utf8', (err, data) => + { + expect(err).to.be.null; + resolve((new window.DOMParser()).parseFromString(data, 'text/xml')); + })); + + const loadImage = (url) => new Promise((resolve) => + { + const image = new Image(); + + image.onload = () => resolve(image); + image.src = resolveURL(url); + }); + + Promise.all([ + loadXML('font.fnt'), + loadXML('font@0.5x.fnt'), + loadImage('font.png'), + loadImage('font@0.5x.png'), + loadImage('atlas.png'), + loadImage('atlas@0.5x.png'), + ]).then(([ + fontXML, + fontScaledXML, + fontImage, + fontScaledImage, + atlasImage, + atlasScaledImage, + ]) => + { + this.fontXML = fontXML; + this.fontScaledXML = fontScaledXML; + this.fontImage = fontImage; + this.fontScaledImage = fontScaledImage; + this.atlasImage = atlasImage; + this.atlasScaledImage = atlasScaledImage; + done(); + }); + }); + it('should exist and return a function', function () { expect(PIXI.loaders.bitmapFontParser).to.be.a('function'); @@ -33,6 +107,206 @@ // TODO: Test the texture cache code path. // TODO: Test the loading texture code path. // TODO: Test data-url code paths. + + it('should properly register bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontImage, null, 1)); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontImage); + expect(charA.texture.frame.x).to.equal(2); + expect(charA.texture.frame.y).to.equal(2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontImage); + expect(charB.texture.frame.x).to.equal(2); + expect(charB.texture.frame.y).to.equal(24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontImage); + expect(charC.texture.frame.x).to.equal(23); + expect(charC.texture.frame.y).to.equal(2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontImage); + expect(charD.texture.frame.x).to.equal(19); + expect(charD.texture.frame.y).to.equal(24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register SCALED bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontScaledImage, null, 0.5)); + const font = PIXI.extras.BitmapText.registerFont(this.fontScaledXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charA.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.width).to.equal(38); // 19 / 0.5 + expect(charA.texture.frame.height).to.equal(40); // 20 / 0.5 + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charB.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charB.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charB.texture.frame.width).to.equal(30); // 15 / 0.5 + expect(charB.texture.frame.height).to.equal(40); // 20 / 0.5 + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charC.texture.frame.x).to.equal(46); // 23 / 0.5 + expect(charC.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charC.texture.frame.width).to.equal(36); // 18 / 0.5 + expect(charC.texture.frame.height).to.equal(40); // 20 / 0.5 + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charD.texture.frame.x).to.equal(38); // 19 / 0.5 + expect(charD.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charD.texture.frame.width).to.equal(34); // 17 / 0.5 + expect(charD.texture.frame.height).to.equal(40); // 20 / 0.5 + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register bitmap font NESTED into spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); + + it('should properly register bitmap font NESTED into SCALED spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasScaledImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasScaledJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); }); describe('PIXI.loaders.parseBitmapFontData', function () diff --git a/test/loaders/resources/atlas.json b/test/loaders/resources/atlas.json new file mode 100644 index 0000000..86e65a5 --- /dev/null +++ b/test/loaders/resources/atlas.json @@ -0,0 +1,25 @@ +{ + "meta": { + "image": "atlas.png", + "size": {"w":256,"h":256}, + "scale": "1" + }, + "frames": { + "resources/test.png": + { + "frame": {"x":2,"y":2,"w":152,"h":188}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":74,"y":36,"w":152,"h":188}, + "sourceSize": {"w":300,"h":225} + }, + "resources/font.png": + { + "frame": {"x":158,"y":2,"w":40,"h":42}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":40,"h":42}, + "sourceSize": {"w":43,"h":46} + } + } +} \ No newline at end of file diff --git a/test/loaders/resources/atlas.png b/test/loaders/resources/atlas.png new file mode 100644 index 0000000..d5e7892 --- /dev/null +++ b/test/loaders/resources/atlas.png Binary files differ diff --git a/test/loaders/resources/atlas@0.5x.json b/test/loaders/resources/atlas@0.5x.json new file mode 100644 index 0000000..ae990a1 --- /dev/null +++ b/test/loaders/resources/atlas@0.5x.json @@ -0,0 +1,25 @@ +{ + "meta": { + "image": "atlas@0.5x.png", + "size": {"w":256,"h":256}, + "scale": "1" + }, + "frames": { + "resources/test.png": + { + "frame": {"x":2,"y":2,"w":152,"h":188}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":74,"y":36,"w":152,"h":188}, + "sourceSize": {"w":300,"h":225} + }, + "resources/font.png": + { + "frame": {"x":158,"y":2,"w":40,"h":42}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":40,"h":42}, + "sourceSize": {"w":43,"h":46} + } + } +} \ No newline at end of file diff --git a/test/loaders/resources/atlas@0.5x.png b/test/loaders/resources/atlas@0.5x.png new file mode 100644 index 0000000..d5e7892 --- /dev/null +++ b/test/loaders/resources/atlas@0.5x.png Binary files differ diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 4d95e15..2420a7d 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -266,6 +266,7 @@ reset() { this.runners.reset.run(); + return this; } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 5e065e2..7cd460c 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -2,6 +2,8 @@ import Program from '../../../shader/Program'; import { BLEND_MODES } from '../../../const'; import settings from '../../../settings'; +import { uid } from '../../../utils'; +import extractUniformsFromSrc from './extractUniformsFromSrc'; // let math = require('../../../math'); /** @@ -49,12 +51,12 @@ this.glShaders = {}; // used for cacheing.. sure there is a better way! - if (!SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) + if (!Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) { - SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); + Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); } - this.glShaderKey = SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; + this.glShaderKey = Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; /** * The padding of the filter. Some filters require extra space to breath such as a blur. @@ -185,3 +187,12 @@ ].join('\n'); } } + +/** + * Used for caching shader IDs + * + * @static + * @private + */ +Filter.SOURCE_KEY_MAP = {}; + diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js index 5c4ca3f..3843739 100644 --- a/src/core/renderers/webgl/systems/StencilSystem.js +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -125,6 +125,6 @@ { super.destroy(this); - this.stencilMaskStack.stencilStack = null; + this.stencilMaskStack = null; } } diff --git a/src/deprecation.js b/src/deprecation.js index d1b689e..209910a 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -601,6 +601,21 @@ return this.generateCanvasTexture(scaleMode, resolution); }; + /** + * @method + * @name PIXI.GroupD8.isSwapWidthHeight + * @see PIXI.GroupD8.isVertical + * @param {number} rotation - The number to check. + * @returns {boolean} Whether or not the direction is vertical + * @deprecated since version 4.6.0 + */ + core.GroupD8.isSwapWidthHeight = function isSwapWidthHeight(rotation) + { + warn('GroupD8.isSwapWidthHeight was renamed to GroupD8.isVertical'); + + return core.GroupD8.isVertical(rotation); + }; + core.RenderTexture.prototype.render = function render(displayObject, matrix, clear, updateTransform) { this.legacyRenderer.render(displayObject, this, clear, matrix, !updateTransform); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 0592b54..96d9f44 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -1,5 +1,6 @@ import * as core from '../core'; import ObservablePoint from '../core/math/ObservablePoint'; +import { getResolutionOfUrl } from '../core/utils'; import settings from '../core/settings'; /** @@ -510,7 +511,8 @@ const data = {}; const info = xml.getElementsByTagName('info')[0]; const common = xml.getElementsByTagName('common')[0]; - const res = texture.baseTexture.resolution || settings.RESOLUTION; + const fileName = xml.getElementsByTagName('page')[0].getAttribute('file'); + const res = getResolutionOfUrl(fileName, settings.RESOLUTION); data.font = info.getAttribute('face'); data.size = parseInt(info.getAttribute('size'), 10); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js index 3f84d6a..136640e 100644 --- a/test/interaction/InteractionManager.js +++ b/test/interaction/InteractionManager.js @@ -279,13 +279,48 @@ removeSpy.restore(); }); - it('should add and remove pointer events to element', function () + it('should add and remove pointer events to element seven times when touch events are supported', function () { const manager = new PIXI.interaction.InteractionManager(sinon.stub()); const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; manager.interactionDOMElement = element; manager.supportsPointerEvents = true; + manager.supportsTouchEvents = true; + + manager.addEvents(); + + expect(element.addEventListener).to.have.been.callCount(7); + expect(element.addEventListener).to.have.been.calledWith('pointerdown'); + expect(element.addEventListener).to.have.been.calledWith('pointerleave'); + expect(element.addEventListener).to.have.been.calledWith('pointerover'); + + expect(element.addEventListener).to.have.been.calledWith('touchstart'); + expect(element.addEventListener).to.have.been.calledWith('touchcancel'); + expect(element.addEventListener).to.have.been.calledWith('touchend'); + expect(element.addEventListener).to.have.been.calledWith('touchmove'); + + manager.removeEvents(); + + expect(element.removeEventListener).to.have.been.callCount(7); + expect(element.removeEventListener).to.have.been.calledWith('pointerdown'); + expect(element.removeEventListener).to.have.been.calledWith('pointerleave'); + expect(element.removeEventListener).to.have.been.calledWith('pointerover'); + + expect(element.removeEventListener).to.have.been.calledWith('touchstart'); + expect(element.removeEventListener).to.have.been.calledWith('touchcancel'); + expect(element.removeEventListener).to.have.been.calledWith('touchend'); + expect(element.removeEventListener).to.have.been.calledWith('touchmove'); + }); + + it('should add and remove pointer events to element three times when touch events are not supported', function () + { + const manager = new PIXI.interaction.InteractionManager(sinon.stub()); + const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; + + manager.interactionDOMElement = element; + manager.supportsPointerEvents = true; + manager.supportsTouchEvents = false; manager.addEvents(); diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js index 7c77343..70703fc 100644 --- a/test/loaders/bitmapFontParser.js +++ b/test/loaders/bitmapFontParser.js @@ -1,7 +1,81 @@ 'use strict'; +const path = require('path'); +const fs = require('fs'); + describe('PIXI.loaders.bitmapFontParser', function () { + afterEach(function () + { + for (var font in PIXI.extras.BitmapText.fonts) + { + delete PIXI.extras.BitmapText.fonts[font]; + } + for (var baseTexture in PIXI.utils.BaseTextureCache) + { + delete PIXI.utils.BaseTextureCache[baseTexture]; + } + for (var texture in PIXI.utils.TextureCache) + { + delete PIXI.utils.TextureCache[texture]; + } + }); + + before(function (done) + { + const resolveURL = (url) => path.resolve(this.resources, url); + + this.resources = path.join(__dirname, 'resources'); + this.fontXML = null; + this.fontScaledXML = null; + this.fontImage = null; + this.fontScaledImage = null; + this.atlasImage = null; + this.atlasScaledImage = null; + this.atlasJSON = require(resolveURL('atlas.json')); // eslint-disable-line global-require + this.atlasScaledJSON = require(resolveURL('atlas@0.5x.json')); // eslint-disable-line global-require + + const loadXML = (url) => new Promise((resolve) => + fs.readFile(resolveURL(url), 'utf8', (err, data) => + { + expect(err).to.be.null; + resolve((new window.DOMParser()).parseFromString(data, 'text/xml')); + })); + + const loadImage = (url) => new Promise((resolve) => + { + const image = new Image(); + + image.onload = () => resolve(image); + image.src = resolveURL(url); + }); + + Promise.all([ + loadXML('font.fnt'), + loadXML('font@0.5x.fnt'), + loadImage('font.png'), + loadImage('font@0.5x.png'), + loadImage('atlas.png'), + loadImage('atlas@0.5x.png'), + ]).then(([ + fontXML, + fontScaledXML, + fontImage, + fontScaledImage, + atlasImage, + atlasScaledImage, + ]) => + { + this.fontXML = fontXML; + this.fontScaledXML = fontScaledXML; + this.fontImage = fontImage; + this.fontScaledImage = fontScaledImage; + this.atlasImage = atlasImage; + this.atlasScaledImage = atlasScaledImage; + done(); + }); + }); + it('should exist and return a function', function () { expect(PIXI.loaders.bitmapFontParser).to.be.a('function'); @@ -33,6 +107,206 @@ // TODO: Test the texture cache code path. // TODO: Test the loading texture code path. // TODO: Test data-url code paths. + + it('should properly register bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontImage, null, 1)); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontImage); + expect(charA.texture.frame.x).to.equal(2); + expect(charA.texture.frame.y).to.equal(2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontImage); + expect(charB.texture.frame.x).to.equal(2); + expect(charB.texture.frame.y).to.equal(24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontImage); + expect(charC.texture.frame.x).to.equal(23); + expect(charC.texture.frame.y).to.equal(2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontImage); + expect(charD.texture.frame.x).to.equal(19); + expect(charD.texture.frame.y).to.equal(24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register SCALED bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontScaledImage, null, 0.5)); + const font = PIXI.extras.BitmapText.registerFont(this.fontScaledXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charA.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.width).to.equal(38); // 19 / 0.5 + expect(charA.texture.frame.height).to.equal(40); // 20 / 0.5 + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charB.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charB.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charB.texture.frame.width).to.equal(30); // 15 / 0.5 + expect(charB.texture.frame.height).to.equal(40); // 20 / 0.5 + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charC.texture.frame.x).to.equal(46); // 23 / 0.5 + expect(charC.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charC.texture.frame.width).to.equal(36); // 18 / 0.5 + expect(charC.texture.frame.height).to.equal(40); // 20 / 0.5 + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charD.texture.frame.x).to.equal(38); // 19 / 0.5 + expect(charD.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charD.texture.frame.width).to.equal(34); // 17 / 0.5 + expect(charD.texture.frame.height).to.equal(40); // 20 / 0.5 + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register bitmap font NESTED into spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); + + it('should properly register bitmap font NESTED into SCALED spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasScaledImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasScaledJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); }); describe('PIXI.loaders.parseBitmapFontData', function () diff --git a/test/loaders/resources/atlas.json b/test/loaders/resources/atlas.json new file mode 100644 index 0000000..86e65a5 --- /dev/null +++ b/test/loaders/resources/atlas.json @@ -0,0 +1,25 @@ +{ + "meta": { + "image": "atlas.png", + "size": {"w":256,"h":256}, + "scale": "1" + }, + "frames": { + "resources/test.png": + { + "frame": {"x":2,"y":2,"w":152,"h":188}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":74,"y":36,"w":152,"h":188}, + "sourceSize": {"w":300,"h":225} + }, + "resources/font.png": + { + "frame": {"x":158,"y":2,"w":40,"h":42}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":40,"h":42}, + "sourceSize": {"w":43,"h":46} + } + } +} \ No newline at end of file diff --git a/test/loaders/resources/atlas.png b/test/loaders/resources/atlas.png new file mode 100644 index 0000000..d5e7892 --- /dev/null +++ b/test/loaders/resources/atlas.png Binary files differ diff --git a/test/loaders/resources/atlas@0.5x.json b/test/loaders/resources/atlas@0.5x.json new file mode 100644 index 0000000..ae990a1 --- /dev/null +++ b/test/loaders/resources/atlas@0.5x.json @@ -0,0 +1,25 @@ +{ + "meta": { + "image": "atlas@0.5x.png", + "size": {"w":256,"h":256}, + "scale": "1" + }, + "frames": { + "resources/test.png": + { + "frame": {"x":2,"y":2,"w":152,"h":188}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":74,"y":36,"w":152,"h":188}, + "sourceSize": {"w":300,"h":225} + }, + "resources/font.png": + { + "frame": {"x":158,"y":2,"w":40,"h":42}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":40,"h":42}, + "sourceSize": {"w":43,"h":46} + } + } +} \ No newline at end of file diff --git a/test/loaders/resources/atlas@0.5x.png b/test/loaders/resources/atlas@0.5x.png new file mode 100644 index 0000000..d5e7892 --- /dev/null +++ b/test/loaders/resources/atlas@0.5x.png Binary files differ diff --git a/test/loaders/resources/font.fnt b/test/loaders/resources/font.fnt new file mode 100644 index 0000000..56e1060 --- /dev/null +++ b/test/loaders/resources/font.fnt @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 4d95e15..2420a7d 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -266,6 +266,7 @@ reset() { this.runners.reset.run(); + return this; } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 5e065e2..7cd460c 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -2,6 +2,8 @@ import Program from '../../../shader/Program'; import { BLEND_MODES } from '../../../const'; import settings from '../../../settings'; +import { uid } from '../../../utils'; +import extractUniformsFromSrc from './extractUniformsFromSrc'; // let math = require('../../../math'); /** @@ -49,12 +51,12 @@ this.glShaders = {}; // used for cacheing.. sure there is a better way! - if (!SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) + if (!Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) { - SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); + Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); } - this.glShaderKey = SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; + this.glShaderKey = Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; /** * The padding of the filter. Some filters require extra space to breath such as a blur. @@ -185,3 +187,12 @@ ].join('\n'); } } + +/** + * Used for caching shader IDs + * + * @static + * @private + */ +Filter.SOURCE_KEY_MAP = {}; + diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js index 5c4ca3f..3843739 100644 --- a/src/core/renderers/webgl/systems/StencilSystem.js +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -125,6 +125,6 @@ { super.destroy(this); - this.stencilMaskStack.stencilStack = null; + this.stencilMaskStack = null; } } diff --git a/src/deprecation.js b/src/deprecation.js index d1b689e..209910a 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -601,6 +601,21 @@ return this.generateCanvasTexture(scaleMode, resolution); }; + /** + * @method + * @name PIXI.GroupD8.isSwapWidthHeight + * @see PIXI.GroupD8.isVertical + * @param {number} rotation - The number to check. + * @returns {boolean} Whether or not the direction is vertical + * @deprecated since version 4.6.0 + */ + core.GroupD8.isSwapWidthHeight = function isSwapWidthHeight(rotation) + { + warn('GroupD8.isSwapWidthHeight was renamed to GroupD8.isVertical'); + + return core.GroupD8.isVertical(rotation); + }; + core.RenderTexture.prototype.render = function render(displayObject, matrix, clear, updateTransform) { this.legacyRenderer.render(displayObject, this, clear, matrix, !updateTransform); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 0592b54..96d9f44 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -1,5 +1,6 @@ import * as core from '../core'; import ObservablePoint from '../core/math/ObservablePoint'; +import { getResolutionOfUrl } from '../core/utils'; import settings from '../core/settings'; /** @@ -510,7 +511,8 @@ const data = {}; const info = xml.getElementsByTagName('info')[0]; const common = xml.getElementsByTagName('common')[0]; - const res = texture.baseTexture.resolution || settings.RESOLUTION; + const fileName = xml.getElementsByTagName('page')[0].getAttribute('file'); + const res = getResolutionOfUrl(fileName, settings.RESOLUTION); data.font = info.getAttribute('face'); data.size = parseInt(info.getAttribute('size'), 10); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js index 3f84d6a..136640e 100644 --- a/test/interaction/InteractionManager.js +++ b/test/interaction/InteractionManager.js @@ -279,13 +279,48 @@ removeSpy.restore(); }); - it('should add and remove pointer events to element', function () + it('should add and remove pointer events to element seven times when touch events are supported', function () { const manager = new PIXI.interaction.InteractionManager(sinon.stub()); const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; manager.interactionDOMElement = element; manager.supportsPointerEvents = true; + manager.supportsTouchEvents = true; + + manager.addEvents(); + + expect(element.addEventListener).to.have.been.callCount(7); + expect(element.addEventListener).to.have.been.calledWith('pointerdown'); + expect(element.addEventListener).to.have.been.calledWith('pointerleave'); + expect(element.addEventListener).to.have.been.calledWith('pointerover'); + + expect(element.addEventListener).to.have.been.calledWith('touchstart'); + expect(element.addEventListener).to.have.been.calledWith('touchcancel'); + expect(element.addEventListener).to.have.been.calledWith('touchend'); + expect(element.addEventListener).to.have.been.calledWith('touchmove'); + + manager.removeEvents(); + + expect(element.removeEventListener).to.have.been.callCount(7); + expect(element.removeEventListener).to.have.been.calledWith('pointerdown'); + expect(element.removeEventListener).to.have.been.calledWith('pointerleave'); + expect(element.removeEventListener).to.have.been.calledWith('pointerover'); + + expect(element.removeEventListener).to.have.been.calledWith('touchstart'); + expect(element.removeEventListener).to.have.been.calledWith('touchcancel'); + expect(element.removeEventListener).to.have.been.calledWith('touchend'); + expect(element.removeEventListener).to.have.been.calledWith('touchmove'); + }); + + it('should add and remove pointer events to element three times when touch events are not supported', function () + { + const manager = new PIXI.interaction.InteractionManager(sinon.stub()); + const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; + + manager.interactionDOMElement = element; + manager.supportsPointerEvents = true; + manager.supportsTouchEvents = false; manager.addEvents(); diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js index 7c77343..70703fc 100644 --- a/test/loaders/bitmapFontParser.js +++ b/test/loaders/bitmapFontParser.js @@ -1,7 +1,81 @@ 'use strict'; +const path = require('path'); +const fs = require('fs'); + describe('PIXI.loaders.bitmapFontParser', function () { + afterEach(function () + { + for (var font in PIXI.extras.BitmapText.fonts) + { + delete PIXI.extras.BitmapText.fonts[font]; + } + for (var baseTexture in PIXI.utils.BaseTextureCache) + { + delete PIXI.utils.BaseTextureCache[baseTexture]; + } + for (var texture in PIXI.utils.TextureCache) + { + delete PIXI.utils.TextureCache[texture]; + } + }); + + before(function (done) + { + const resolveURL = (url) => path.resolve(this.resources, url); + + this.resources = path.join(__dirname, 'resources'); + this.fontXML = null; + this.fontScaledXML = null; + this.fontImage = null; + this.fontScaledImage = null; + this.atlasImage = null; + this.atlasScaledImage = null; + this.atlasJSON = require(resolveURL('atlas.json')); // eslint-disable-line global-require + this.atlasScaledJSON = require(resolveURL('atlas@0.5x.json')); // eslint-disable-line global-require + + const loadXML = (url) => new Promise((resolve) => + fs.readFile(resolveURL(url), 'utf8', (err, data) => + { + expect(err).to.be.null; + resolve((new window.DOMParser()).parseFromString(data, 'text/xml')); + })); + + const loadImage = (url) => new Promise((resolve) => + { + const image = new Image(); + + image.onload = () => resolve(image); + image.src = resolveURL(url); + }); + + Promise.all([ + loadXML('font.fnt'), + loadXML('font@0.5x.fnt'), + loadImage('font.png'), + loadImage('font@0.5x.png'), + loadImage('atlas.png'), + loadImage('atlas@0.5x.png'), + ]).then(([ + fontXML, + fontScaledXML, + fontImage, + fontScaledImage, + atlasImage, + atlasScaledImage, + ]) => + { + this.fontXML = fontXML; + this.fontScaledXML = fontScaledXML; + this.fontImage = fontImage; + this.fontScaledImage = fontScaledImage; + this.atlasImage = atlasImage; + this.atlasScaledImage = atlasScaledImage; + done(); + }); + }); + it('should exist and return a function', function () { expect(PIXI.loaders.bitmapFontParser).to.be.a('function'); @@ -33,6 +107,206 @@ // TODO: Test the texture cache code path. // TODO: Test the loading texture code path. // TODO: Test data-url code paths. + + it('should properly register bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontImage, null, 1)); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontImage); + expect(charA.texture.frame.x).to.equal(2); + expect(charA.texture.frame.y).to.equal(2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontImage); + expect(charB.texture.frame.x).to.equal(2); + expect(charB.texture.frame.y).to.equal(24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontImage); + expect(charC.texture.frame.x).to.equal(23); + expect(charC.texture.frame.y).to.equal(2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontImage); + expect(charD.texture.frame.x).to.equal(19); + expect(charD.texture.frame.y).to.equal(24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register SCALED bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontScaledImage, null, 0.5)); + const font = PIXI.extras.BitmapText.registerFont(this.fontScaledXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charA.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.width).to.equal(38); // 19 / 0.5 + expect(charA.texture.frame.height).to.equal(40); // 20 / 0.5 + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charB.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charB.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charB.texture.frame.width).to.equal(30); // 15 / 0.5 + expect(charB.texture.frame.height).to.equal(40); // 20 / 0.5 + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charC.texture.frame.x).to.equal(46); // 23 / 0.5 + expect(charC.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charC.texture.frame.width).to.equal(36); // 18 / 0.5 + expect(charC.texture.frame.height).to.equal(40); // 20 / 0.5 + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charD.texture.frame.x).to.equal(38); // 19 / 0.5 + expect(charD.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charD.texture.frame.width).to.equal(34); // 17 / 0.5 + expect(charD.texture.frame.height).to.equal(40); // 20 / 0.5 + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register bitmap font NESTED into spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); + + it('should properly register bitmap font NESTED into SCALED spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasScaledImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasScaledJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); }); describe('PIXI.loaders.parseBitmapFontData', function () diff --git a/test/loaders/resources/atlas.json b/test/loaders/resources/atlas.json new file mode 100644 index 0000000..86e65a5 --- /dev/null +++ b/test/loaders/resources/atlas.json @@ -0,0 +1,25 @@ +{ + "meta": { + "image": "atlas.png", + "size": {"w":256,"h":256}, + "scale": "1" + }, + "frames": { + "resources/test.png": + { + "frame": {"x":2,"y":2,"w":152,"h":188}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":74,"y":36,"w":152,"h":188}, + "sourceSize": {"w":300,"h":225} + }, + "resources/font.png": + { + "frame": {"x":158,"y":2,"w":40,"h":42}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":40,"h":42}, + "sourceSize": {"w":43,"h":46} + } + } +} \ No newline at end of file diff --git a/test/loaders/resources/atlas.png b/test/loaders/resources/atlas.png new file mode 100644 index 0000000..d5e7892 --- /dev/null +++ b/test/loaders/resources/atlas.png Binary files differ diff --git a/test/loaders/resources/atlas@0.5x.json b/test/loaders/resources/atlas@0.5x.json new file mode 100644 index 0000000..ae990a1 --- /dev/null +++ b/test/loaders/resources/atlas@0.5x.json @@ -0,0 +1,25 @@ +{ + "meta": { + "image": "atlas@0.5x.png", + "size": {"w":256,"h":256}, + "scale": "1" + }, + "frames": { + "resources/test.png": + { + "frame": {"x":2,"y":2,"w":152,"h":188}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":74,"y":36,"w":152,"h":188}, + "sourceSize": {"w":300,"h":225} + }, + "resources/font.png": + { + "frame": {"x":158,"y":2,"w":40,"h":42}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":40,"h":42}, + "sourceSize": {"w":43,"h":46} + } + } +} \ No newline at end of file diff --git a/test/loaders/resources/atlas@0.5x.png b/test/loaders/resources/atlas@0.5x.png new file mode 100644 index 0000000..d5e7892 --- /dev/null +++ b/test/loaders/resources/atlas@0.5x.png Binary files differ diff --git a/test/loaders/resources/font.fnt b/test/loaders/resources/font.fnt new file mode 100644 index 0000000..56e1060 --- /dev/null +++ b/test/loaders/resources/font.fnt @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/loaders/resources/font.png b/test/loaders/resources/font.png new file mode 100644 index 0000000..cf772e9 --- /dev/null +++ b/test/loaders/resources/font.png Binary files differ diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 4d95e15..2420a7d 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -266,6 +266,7 @@ reset() { this.runners.reset.run(); + return this; } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 5e065e2..7cd460c 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -2,6 +2,8 @@ import Program from '../../../shader/Program'; import { BLEND_MODES } from '../../../const'; import settings from '../../../settings'; +import { uid } from '../../../utils'; +import extractUniformsFromSrc from './extractUniformsFromSrc'; // let math = require('../../../math'); /** @@ -49,12 +51,12 @@ this.glShaders = {}; // used for cacheing.. sure there is a better way! - if (!SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) + if (!Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) { - SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); + Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); } - this.glShaderKey = SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; + this.glShaderKey = Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; /** * The padding of the filter. Some filters require extra space to breath such as a blur. @@ -185,3 +187,12 @@ ].join('\n'); } } + +/** + * Used for caching shader IDs + * + * @static + * @private + */ +Filter.SOURCE_KEY_MAP = {}; + diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js index 5c4ca3f..3843739 100644 --- a/src/core/renderers/webgl/systems/StencilSystem.js +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -125,6 +125,6 @@ { super.destroy(this); - this.stencilMaskStack.stencilStack = null; + this.stencilMaskStack = null; } } diff --git a/src/deprecation.js b/src/deprecation.js index d1b689e..209910a 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -601,6 +601,21 @@ return this.generateCanvasTexture(scaleMode, resolution); }; + /** + * @method + * @name PIXI.GroupD8.isSwapWidthHeight + * @see PIXI.GroupD8.isVertical + * @param {number} rotation - The number to check. + * @returns {boolean} Whether or not the direction is vertical + * @deprecated since version 4.6.0 + */ + core.GroupD8.isSwapWidthHeight = function isSwapWidthHeight(rotation) + { + warn('GroupD8.isSwapWidthHeight was renamed to GroupD8.isVertical'); + + return core.GroupD8.isVertical(rotation); + }; + core.RenderTexture.prototype.render = function render(displayObject, matrix, clear, updateTransform) { this.legacyRenderer.render(displayObject, this, clear, matrix, !updateTransform); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 0592b54..96d9f44 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -1,5 +1,6 @@ import * as core from '../core'; import ObservablePoint from '../core/math/ObservablePoint'; +import { getResolutionOfUrl } from '../core/utils'; import settings from '../core/settings'; /** @@ -510,7 +511,8 @@ const data = {}; const info = xml.getElementsByTagName('info')[0]; const common = xml.getElementsByTagName('common')[0]; - const res = texture.baseTexture.resolution || settings.RESOLUTION; + const fileName = xml.getElementsByTagName('page')[0].getAttribute('file'); + const res = getResolutionOfUrl(fileName, settings.RESOLUTION); data.font = info.getAttribute('face'); data.size = parseInt(info.getAttribute('size'), 10); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js index 3f84d6a..136640e 100644 --- a/test/interaction/InteractionManager.js +++ b/test/interaction/InteractionManager.js @@ -279,13 +279,48 @@ removeSpy.restore(); }); - it('should add and remove pointer events to element', function () + it('should add and remove pointer events to element seven times when touch events are supported', function () { const manager = new PIXI.interaction.InteractionManager(sinon.stub()); const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; manager.interactionDOMElement = element; manager.supportsPointerEvents = true; + manager.supportsTouchEvents = true; + + manager.addEvents(); + + expect(element.addEventListener).to.have.been.callCount(7); + expect(element.addEventListener).to.have.been.calledWith('pointerdown'); + expect(element.addEventListener).to.have.been.calledWith('pointerleave'); + expect(element.addEventListener).to.have.been.calledWith('pointerover'); + + expect(element.addEventListener).to.have.been.calledWith('touchstart'); + expect(element.addEventListener).to.have.been.calledWith('touchcancel'); + expect(element.addEventListener).to.have.been.calledWith('touchend'); + expect(element.addEventListener).to.have.been.calledWith('touchmove'); + + manager.removeEvents(); + + expect(element.removeEventListener).to.have.been.callCount(7); + expect(element.removeEventListener).to.have.been.calledWith('pointerdown'); + expect(element.removeEventListener).to.have.been.calledWith('pointerleave'); + expect(element.removeEventListener).to.have.been.calledWith('pointerover'); + + expect(element.removeEventListener).to.have.been.calledWith('touchstart'); + expect(element.removeEventListener).to.have.been.calledWith('touchcancel'); + expect(element.removeEventListener).to.have.been.calledWith('touchend'); + expect(element.removeEventListener).to.have.been.calledWith('touchmove'); + }); + + it('should add and remove pointer events to element three times when touch events are not supported', function () + { + const manager = new PIXI.interaction.InteractionManager(sinon.stub()); + const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; + + manager.interactionDOMElement = element; + manager.supportsPointerEvents = true; + manager.supportsTouchEvents = false; manager.addEvents(); diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js index 7c77343..70703fc 100644 --- a/test/loaders/bitmapFontParser.js +++ b/test/loaders/bitmapFontParser.js @@ -1,7 +1,81 @@ 'use strict'; +const path = require('path'); +const fs = require('fs'); + describe('PIXI.loaders.bitmapFontParser', function () { + afterEach(function () + { + for (var font in PIXI.extras.BitmapText.fonts) + { + delete PIXI.extras.BitmapText.fonts[font]; + } + for (var baseTexture in PIXI.utils.BaseTextureCache) + { + delete PIXI.utils.BaseTextureCache[baseTexture]; + } + for (var texture in PIXI.utils.TextureCache) + { + delete PIXI.utils.TextureCache[texture]; + } + }); + + before(function (done) + { + const resolveURL = (url) => path.resolve(this.resources, url); + + this.resources = path.join(__dirname, 'resources'); + this.fontXML = null; + this.fontScaledXML = null; + this.fontImage = null; + this.fontScaledImage = null; + this.atlasImage = null; + this.atlasScaledImage = null; + this.atlasJSON = require(resolveURL('atlas.json')); // eslint-disable-line global-require + this.atlasScaledJSON = require(resolveURL('atlas@0.5x.json')); // eslint-disable-line global-require + + const loadXML = (url) => new Promise((resolve) => + fs.readFile(resolveURL(url), 'utf8', (err, data) => + { + expect(err).to.be.null; + resolve((new window.DOMParser()).parseFromString(data, 'text/xml')); + })); + + const loadImage = (url) => new Promise((resolve) => + { + const image = new Image(); + + image.onload = () => resolve(image); + image.src = resolveURL(url); + }); + + Promise.all([ + loadXML('font.fnt'), + loadXML('font@0.5x.fnt'), + loadImage('font.png'), + loadImage('font@0.5x.png'), + loadImage('atlas.png'), + loadImage('atlas@0.5x.png'), + ]).then(([ + fontXML, + fontScaledXML, + fontImage, + fontScaledImage, + atlasImage, + atlasScaledImage, + ]) => + { + this.fontXML = fontXML; + this.fontScaledXML = fontScaledXML; + this.fontImage = fontImage; + this.fontScaledImage = fontScaledImage; + this.atlasImage = atlasImage; + this.atlasScaledImage = atlasScaledImage; + done(); + }); + }); + it('should exist and return a function', function () { expect(PIXI.loaders.bitmapFontParser).to.be.a('function'); @@ -33,6 +107,206 @@ // TODO: Test the texture cache code path. // TODO: Test the loading texture code path. // TODO: Test data-url code paths. + + it('should properly register bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontImage, null, 1)); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontImage); + expect(charA.texture.frame.x).to.equal(2); + expect(charA.texture.frame.y).to.equal(2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontImage); + expect(charB.texture.frame.x).to.equal(2); + expect(charB.texture.frame.y).to.equal(24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontImage); + expect(charC.texture.frame.x).to.equal(23); + expect(charC.texture.frame.y).to.equal(2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontImage); + expect(charD.texture.frame.x).to.equal(19); + expect(charD.texture.frame.y).to.equal(24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register SCALED bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontScaledImage, null, 0.5)); + const font = PIXI.extras.BitmapText.registerFont(this.fontScaledXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charA.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.width).to.equal(38); // 19 / 0.5 + expect(charA.texture.frame.height).to.equal(40); // 20 / 0.5 + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charB.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charB.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charB.texture.frame.width).to.equal(30); // 15 / 0.5 + expect(charB.texture.frame.height).to.equal(40); // 20 / 0.5 + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charC.texture.frame.x).to.equal(46); // 23 / 0.5 + expect(charC.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charC.texture.frame.width).to.equal(36); // 18 / 0.5 + expect(charC.texture.frame.height).to.equal(40); // 20 / 0.5 + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charD.texture.frame.x).to.equal(38); // 19 / 0.5 + expect(charD.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charD.texture.frame.width).to.equal(34); // 17 / 0.5 + expect(charD.texture.frame.height).to.equal(40); // 20 / 0.5 + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register bitmap font NESTED into spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); + + it('should properly register bitmap font NESTED into SCALED spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasScaledImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasScaledJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); }); describe('PIXI.loaders.parseBitmapFontData', function () diff --git a/test/loaders/resources/atlas.json b/test/loaders/resources/atlas.json new file mode 100644 index 0000000..86e65a5 --- /dev/null +++ b/test/loaders/resources/atlas.json @@ -0,0 +1,25 @@ +{ + "meta": { + "image": "atlas.png", + "size": {"w":256,"h":256}, + "scale": "1" + }, + "frames": { + "resources/test.png": + { + "frame": {"x":2,"y":2,"w":152,"h":188}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":74,"y":36,"w":152,"h":188}, + "sourceSize": {"w":300,"h":225} + }, + "resources/font.png": + { + "frame": {"x":158,"y":2,"w":40,"h":42}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":40,"h":42}, + "sourceSize": {"w":43,"h":46} + } + } +} \ No newline at end of file diff --git a/test/loaders/resources/atlas.png b/test/loaders/resources/atlas.png new file mode 100644 index 0000000..d5e7892 --- /dev/null +++ b/test/loaders/resources/atlas.png Binary files differ diff --git a/test/loaders/resources/atlas@0.5x.json b/test/loaders/resources/atlas@0.5x.json new file mode 100644 index 0000000..ae990a1 --- /dev/null +++ b/test/loaders/resources/atlas@0.5x.json @@ -0,0 +1,25 @@ +{ + "meta": { + "image": "atlas@0.5x.png", + "size": {"w":256,"h":256}, + "scale": "1" + }, + "frames": { + "resources/test.png": + { + "frame": {"x":2,"y":2,"w":152,"h":188}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":74,"y":36,"w":152,"h":188}, + "sourceSize": {"w":300,"h":225} + }, + "resources/font.png": + { + "frame": {"x":158,"y":2,"w":40,"h":42}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":40,"h":42}, + "sourceSize": {"w":43,"h":46} + } + } +} \ No newline at end of file diff --git a/test/loaders/resources/atlas@0.5x.png b/test/loaders/resources/atlas@0.5x.png new file mode 100644 index 0000000..d5e7892 --- /dev/null +++ b/test/loaders/resources/atlas@0.5x.png Binary files differ diff --git a/test/loaders/resources/font.fnt b/test/loaders/resources/font.fnt new file mode 100644 index 0000000..56e1060 --- /dev/null +++ b/test/loaders/resources/font.fnt @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/loaders/resources/font.png b/test/loaders/resources/font.png new file mode 100644 index 0000000..cf772e9 --- /dev/null +++ b/test/loaders/resources/font.png Binary files differ diff --git a/test/loaders/resources/font@0.5x.fnt b/test/loaders/resources/font@0.5x.fnt new file mode 100644 index 0000000..6c247c7 --- /dev/null +++ b/test/loaders/resources/font@0.5x.fnt @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/core/math/GroupD8.js b/src/core/math/GroupD8.js index 50c2f31..e6a09e7 100644 --- a/src/core/math/GroupD8.js +++ b/src/core/math/GroupD8.js @@ -111,13 +111,14 @@ rotate180: (rotation) => rotation ^ 4, /** - * I dont know why sometimes width and heights needs to be swapped. We'll fix it later. + * Direction of main vector can be horizontal, vertical or diagonal. + * Some objects work with vertical directions different. * * @memberof PIXI.GroupD8 * @param {number} rotation - The number to check. - * @returns {boolean} Whether or not the width/height should be swapped. + * @returns {boolean} Whether or not the direction is vertical */ - isSwapWidthHeight: (rotation) => (rotation & 3) === 2, + isVertical: (rotation) => (rotation & 3) === 2, /** * @memberof PIXI.GroupD8 diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 4d95e15..2420a7d 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -266,6 +266,7 @@ reset() { this.runners.reset.run(); + return this; } diff --git a/src/core/renderers/webgl/filters/Filter.js b/src/core/renderers/webgl/filters/Filter.js index 5e065e2..7cd460c 100644 --- a/src/core/renderers/webgl/filters/Filter.js +++ b/src/core/renderers/webgl/filters/Filter.js @@ -2,6 +2,8 @@ import Program from '../../../shader/Program'; import { BLEND_MODES } from '../../../const'; import settings from '../../../settings'; +import { uid } from '../../../utils'; +import extractUniformsFromSrc from './extractUniformsFromSrc'; // let math = require('../../../math'); /** @@ -49,12 +51,12 @@ this.glShaders = {}; // used for cacheing.. sure there is a better way! - if (!SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) + if (!Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]) { - SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); + Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc] = uid(); } - this.glShaderKey = SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; + this.glShaderKey = Filter.SOURCE_KEY_MAP[this.vertexSrc + this.fragmentSrc]; /** * The padding of the filter. Some filters require extra space to breath such as a blur. @@ -185,3 +187,12 @@ ].join('\n'); } } + +/** + * Used for caching shader IDs + * + * @static + * @private + */ +Filter.SOURCE_KEY_MAP = {}; + diff --git a/src/core/renderers/webgl/systems/StencilSystem.js b/src/core/renderers/webgl/systems/StencilSystem.js index 5c4ca3f..3843739 100644 --- a/src/core/renderers/webgl/systems/StencilSystem.js +++ b/src/core/renderers/webgl/systems/StencilSystem.js @@ -125,6 +125,6 @@ { super.destroy(this); - this.stencilMaskStack.stencilStack = null; + this.stencilMaskStack = null; } } diff --git a/src/deprecation.js b/src/deprecation.js index d1b689e..209910a 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -601,6 +601,21 @@ return this.generateCanvasTexture(scaleMode, resolution); }; + /** + * @method + * @name PIXI.GroupD8.isSwapWidthHeight + * @see PIXI.GroupD8.isVertical + * @param {number} rotation - The number to check. + * @returns {boolean} Whether or not the direction is vertical + * @deprecated since version 4.6.0 + */ + core.GroupD8.isSwapWidthHeight = function isSwapWidthHeight(rotation) + { + warn('GroupD8.isSwapWidthHeight was renamed to GroupD8.isVertical'); + + return core.GroupD8.isVertical(rotation); + }; + core.RenderTexture.prototype.render = function render(displayObject, matrix, clear, updateTransform) { this.legacyRenderer.render(displayObject, this, clear, matrix, !updateTransform); diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 0592b54..96d9f44 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -1,5 +1,6 @@ import * as core from '../core'; import ObservablePoint from '../core/math/ObservablePoint'; +import { getResolutionOfUrl } from '../core/utils'; import settings from '../core/settings'; /** @@ -510,7 +511,8 @@ const data = {}; const info = xml.getElementsByTagName('info')[0]; const common = xml.getElementsByTagName('common')[0]; - const res = texture.baseTexture.resolution || settings.RESOLUTION; + const fileName = xml.getElementsByTagName('page')[0].getAttribute('file'); + const res = getResolutionOfUrl(fileName, settings.RESOLUTION); data.font = info.getAttribute('face'); data.size = parseInt(info.getAttribute('size'), 10); diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js index 3f84d6a..136640e 100644 --- a/test/interaction/InteractionManager.js +++ b/test/interaction/InteractionManager.js @@ -279,13 +279,48 @@ removeSpy.restore(); }); - it('should add and remove pointer events to element', function () + it('should add and remove pointer events to element seven times when touch events are supported', function () { const manager = new PIXI.interaction.InteractionManager(sinon.stub()); const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; manager.interactionDOMElement = element; manager.supportsPointerEvents = true; + manager.supportsTouchEvents = true; + + manager.addEvents(); + + expect(element.addEventListener).to.have.been.callCount(7); + expect(element.addEventListener).to.have.been.calledWith('pointerdown'); + expect(element.addEventListener).to.have.been.calledWith('pointerleave'); + expect(element.addEventListener).to.have.been.calledWith('pointerover'); + + expect(element.addEventListener).to.have.been.calledWith('touchstart'); + expect(element.addEventListener).to.have.been.calledWith('touchcancel'); + expect(element.addEventListener).to.have.been.calledWith('touchend'); + expect(element.addEventListener).to.have.been.calledWith('touchmove'); + + manager.removeEvents(); + + expect(element.removeEventListener).to.have.been.callCount(7); + expect(element.removeEventListener).to.have.been.calledWith('pointerdown'); + expect(element.removeEventListener).to.have.been.calledWith('pointerleave'); + expect(element.removeEventListener).to.have.been.calledWith('pointerover'); + + expect(element.removeEventListener).to.have.been.calledWith('touchstart'); + expect(element.removeEventListener).to.have.been.calledWith('touchcancel'); + expect(element.removeEventListener).to.have.been.calledWith('touchend'); + expect(element.removeEventListener).to.have.been.calledWith('touchmove'); + }); + + it('should add and remove pointer events to element three times when touch events are not supported', function () + { + const manager = new PIXI.interaction.InteractionManager(sinon.stub()); + const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() }; + + manager.interactionDOMElement = element; + manager.supportsPointerEvents = true; + manager.supportsTouchEvents = false; manager.addEvents(); diff --git a/test/loaders/bitmapFontParser.js b/test/loaders/bitmapFontParser.js index 7c77343..70703fc 100644 --- a/test/loaders/bitmapFontParser.js +++ b/test/loaders/bitmapFontParser.js @@ -1,7 +1,81 @@ 'use strict'; +const path = require('path'); +const fs = require('fs'); + describe('PIXI.loaders.bitmapFontParser', function () { + afterEach(function () + { + for (var font in PIXI.extras.BitmapText.fonts) + { + delete PIXI.extras.BitmapText.fonts[font]; + } + for (var baseTexture in PIXI.utils.BaseTextureCache) + { + delete PIXI.utils.BaseTextureCache[baseTexture]; + } + for (var texture in PIXI.utils.TextureCache) + { + delete PIXI.utils.TextureCache[texture]; + } + }); + + before(function (done) + { + const resolveURL = (url) => path.resolve(this.resources, url); + + this.resources = path.join(__dirname, 'resources'); + this.fontXML = null; + this.fontScaledXML = null; + this.fontImage = null; + this.fontScaledImage = null; + this.atlasImage = null; + this.atlasScaledImage = null; + this.atlasJSON = require(resolveURL('atlas.json')); // eslint-disable-line global-require + this.atlasScaledJSON = require(resolveURL('atlas@0.5x.json')); // eslint-disable-line global-require + + const loadXML = (url) => new Promise((resolve) => + fs.readFile(resolveURL(url), 'utf8', (err, data) => + { + expect(err).to.be.null; + resolve((new window.DOMParser()).parseFromString(data, 'text/xml')); + })); + + const loadImage = (url) => new Promise((resolve) => + { + const image = new Image(); + + image.onload = () => resolve(image); + image.src = resolveURL(url); + }); + + Promise.all([ + loadXML('font.fnt'), + loadXML('font@0.5x.fnt'), + loadImage('font.png'), + loadImage('font@0.5x.png'), + loadImage('atlas.png'), + loadImage('atlas@0.5x.png'), + ]).then(([ + fontXML, + fontScaledXML, + fontImage, + fontScaledImage, + atlasImage, + atlasScaledImage, + ]) => + { + this.fontXML = fontXML; + this.fontScaledXML = fontScaledXML; + this.fontImage = fontImage; + this.fontScaledImage = fontScaledImage; + this.atlasImage = atlasImage; + this.atlasScaledImage = atlasScaledImage; + done(); + }); + }); + it('should exist and return a function', function () { expect(PIXI.loaders.bitmapFontParser).to.be.a('function'); @@ -33,6 +107,206 @@ // TODO: Test the texture cache code path. // TODO: Test the loading texture code path. // TODO: Test data-url code paths. + + it('should properly register bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontImage, null, 1)); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontImage); + expect(charA.texture.frame.x).to.equal(2); + expect(charA.texture.frame.y).to.equal(2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontImage); + expect(charB.texture.frame.x).to.equal(2); + expect(charB.texture.frame.y).to.equal(24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontImage); + expect(charC.texture.frame.x).to.equal(23); + expect(charC.texture.frame.y).to.equal(2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontImage); + expect(charD.texture.frame.x).to.equal(19); + expect(charD.texture.frame.y).to.equal(24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register SCALED bitmap font', function (done) + { + const texture = new PIXI.Texture(new PIXI.BaseTexture(this.fontScaledImage, null, 0.5)); + const font = PIXI.extras.BitmapText.registerFont(this.fontScaledXML, texture); + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charA.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charA.texture.frame.width).to.equal(38); // 19 / 0.5 + expect(charA.texture.frame.height).to.equal(40); // 20 / 0.5 + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charB.texture.frame.x).to.equal(4); // 2 / 0.5 + expect(charB.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charB.texture.frame.width).to.equal(30); // 15 / 0.5 + expect(charB.texture.frame.height).to.equal(40); // 20 / 0.5 + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charC.texture.frame.x).to.equal(46); // 23 / 0.5 + expect(charC.texture.frame.y).to.equal(4); // 2 / 0.5 + expect(charC.texture.frame.width).to.equal(36); // 18 / 0.5 + expect(charC.texture.frame.height).to.equal(40); // 20 / 0.5 + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.fontScaledImage); + expect(charD.texture.frame.x).to.equal(38); // 19 / 0.5 + expect(charD.texture.frame.y).to.equal(48); // 24 / 0.5 + expect(charD.texture.frame.width).to.equal(34); // 17 / 0.5 + expect(charD.texture.frame.height).to.equal(40); // 20 / 0.5 + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + + it('should properly register bitmap font NESTED into spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); + + it('should properly register bitmap font NESTED into SCALED spritesheet', function (done) + { + const baseTexture = new PIXI.BaseTexture(this.atlasScaledImage, null, 1); + const spritesheet = new PIXI.Spritesheet(baseTexture, this.atlasScaledJSON); + + spritesheet.parse(() => + { + const fontTexture = PIXI.Texture.fromFrame('resources/font.png'); + const font = PIXI.extras.BitmapText.registerFont(this.fontXML, fontTexture); + const fontX = 158; // bare value from spritesheet frame + const fontY = 2; // bare value from spritesheet frame + + expect(font).to.be.an.object; + expect(PIXI.extras.BitmapText.fonts.font).to.equal(font); + expect(font).to.have.property('chars'); + const charA = font.chars['A'.charCodeAt(0) || 65]; + + expect(charA).to.exist; + expect(charA.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charA.texture.frame.x).to.equal(fontX + 2); + expect(charA.texture.frame.y).to.equal(fontY + 2); + expect(charA.texture.frame.width).to.equal(19); + expect(charA.texture.frame.height).to.equal(20); + const charB = font.chars['B'.charCodeAt(0) || 66]; + + expect(charB).to.exist; + expect(charB.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charB.texture.frame.x).to.equal(fontX + 2); + expect(charB.texture.frame.y).to.equal(fontY + 24); + expect(charB.texture.frame.width).to.equal(15); + expect(charB.texture.frame.height).to.equal(20); + const charC = font.chars['C'.charCodeAt(0) || 67]; + + expect(charC).to.exist; + expect(charC.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charC.texture.frame.x).to.equal(fontX + 23); + expect(charC.texture.frame.y).to.equal(fontY + 2); + expect(charC.texture.frame.width).to.equal(18); + expect(charC.texture.frame.height).to.equal(20); + const charD = font.chars['D'.charCodeAt(0) || 68]; + + expect(charD).to.exist; + expect(charD.texture.baseTexture.source).to.equal(this.atlasScaledImage); + expect(charD.texture.frame.x).to.equal(fontX + 19); + expect(charD.texture.frame.y).to.equal(fontY + 24); + expect(charD.texture.frame.width).to.equal(17); + expect(charD.texture.frame.height).to.equal(20); + const charE = font.chars['E'.charCodeAt(0) || 69]; + + expect(charE).to.be.undefined; + done(); + }); + }); }); describe('PIXI.loaders.parseBitmapFontData', function () diff --git a/test/loaders/resources/atlas.json b/test/loaders/resources/atlas.json new file mode 100644 index 0000000..86e65a5 --- /dev/null +++ b/test/loaders/resources/atlas.json @@ -0,0 +1,25 @@ +{ + "meta": { + "image": "atlas.png", + "size": {"w":256,"h":256}, + "scale": "1" + }, + "frames": { + "resources/test.png": + { + "frame": {"x":2,"y":2,"w":152,"h":188}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":74,"y":36,"w":152,"h":188}, + "sourceSize": {"w":300,"h":225} + }, + "resources/font.png": + { + "frame": {"x":158,"y":2,"w":40,"h":42}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":40,"h":42}, + "sourceSize": {"w":43,"h":46} + } + } +} \ No newline at end of file diff --git a/test/loaders/resources/atlas.png b/test/loaders/resources/atlas.png new file mode 100644 index 0000000..d5e7892 --- /dev/null +++ b/test/loaders/resources/atlas.png Binary files differ diff --git a/test/loaders/resources/atlas@0.5x.json b/test/loaders/resources/atlas@0.5x.json new file mode 100644 index 0000000..ae990a1 --- /dev/null +++ b/test/loaders/resources/atlas@0.5x.json @@ -0,0 +1,25 @@ +{ + "meta": { + "image": "atlas@0.5x.png", + "size": {"w":256,"h":256}, + "scale": "1" + }, + "frames": { + "resources/test.png": + { + "frame": {"x":2,"y":2,"w":152,"h":188}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":74,"y":36,"w":152,"h":188}, + "sourceSize": {"w":300,"h":225} + }, + "resources/font.png": + { + "frame": {"x":158,"y":2,"w":40,"h":42}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":40,"h":42}, + "sourceSize": {"w":43,"h":46} + } + } +} \ No newline at end of file diff --git a/test/loaders/resources/atlas@0.5x.png b/test/loaders/resources/atlas@0.5x.png new file mode 100644 index 0000000..d5e7892 --- /dev/null +++ b/test/loaders/resources/atlas@0.5x.png Binary files differ diff --git a/test/loaders/resources/font.fnt b/test/loaders/resources/font.fnt new file mode 100644 index 0000000..56e1060 --- /dev/null +++ b/test/loaders/resources/font.fnt @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/loaders/resources/font.png b/test/loaders/resources/font.png new file mode 100644 index 0000000..cf772e9 --- /dev/null +++ b/test/loaders/resources/font.png Binary files differ diff --git a/test/loaders/resources/font@0.5x.fnt b/test/loaders/resources/font@0.5x.fnt new file mode 100644 index 0000000..6c247c7 --- /dev/null +++ b/test/loaders/resources/font@0.5x.fnt @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/loaders/resources/font@0.5x.png b/test/loaders/resources/font@0.5x.png new file mode 100644 index 0000000..cf772e9 --- /dev/null +++ b/test/loaders/resources/font@0.5x.png Binary files differ