diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 1340fcc..0592b54 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -96,16 +96,18 @@ * Disable by setting value to 0 * * @member {number} + * @private */ - this.maxWidth = 0; + this._maxWidth = 0; /** * The max line height. This is useful when trying to use the total height of the Text, * ie: when trying to vertically align. * * @member {number} + * @private */ - this.maxLineHeight = 0; + this._maxLineHeight = 0; /** * Text anchor. read-only @@ -144,6 +146,7 @@ let line = 0; let lastSpace = -1; let lastSpaceWidth = 0; + let spacesRemoved = 0; let maxLineHeight = 0; for (let i = 0; i < this.text.length; i++) @@ -168,11 +171,12 @@ continue; } - if (lastSpace !== -1 && this.maxWidth > 0 && pos.x * scale > this.maxWidth) + if (lastSpace !== -1 && this._maxWidth > 0 && pos.x * scale > this._maxWidth) { - core.utils.removeItems(chars, lastSpace, i - lastSpace); + core.utils.removeItems(chars, lastSpace - spacesRemoved, i - lastSpace); i = lastSpace; lastSpace = -1; + ++spacesRemoved; lineWidths.push(lastSpaceWidth); maxLineWidth = Math.max(maxLineWidth, lastSpaceWidth); @@ -275,7 +279,7 @@ this._glyphs[i].y -= this._textHeight * this.anchor.y; } } - this.maxLineHeight = maxLineHeight * scale; + this._maxLineHeight = maxLineHeight * scale; } /** @@ -430,6 +434,42 @@ } /** + * The max width of this bitmap text in pixels. If the text provided is longer than the + * value provided, line breaks will be automatically inserted in the last whitespace. + * Disable by setting value to 0 + * + * @member {number} + */ + get maxWidth() + { + return this._maxWidth; + } + + set maxWidth(value) // eslint-disable-line require-jsdoc + { + if (this._maxWidth === value) + { + return; + } + this._maxWidth = value; + this.dirty = true; + } + + /** + * The max line height. This is useful when trying to use the total height of the Text, + * ie: when trying to vertically align. + * + * @member {number} + * @readonly + */ + get maxLineHeight() + { + this.validate(); + + return this._maxLineHeight; + } + + /** * The width of the overall text, different from fontSize, * which is defined in the style object * diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 1340fcc..0592b54 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -96,16 +96,18 @@ * Disable by setting value to 0 * * @member {number} + * @private */ - this.maxWidth = 0; + this._maxWidth = 0; /** * The max line height. This is useful when trying to use the total height of the Text, * ie: when trying to vertically align. * * @member {number} + * @private */ - this.maxLineHeight = 0; + this._maxLineHeight = 0; /** * Text anchor. read-only @@ -144,6 +146,7 @@ let line = 0; let lastSpace = -1; let lastSpaceWidth = 0; + let spacesRemoved = 0; let maxLineHeight = 0; for (let i = 0; i < this.text.length; i++) @@ -168,11 +171,12 @@ continue; } - if (lastSpace !== -1 && this.maxWidth > 0 && pos.x * scale > this.maxWidth) + if (lastSpace !== -1 && this._maxWidth > 0 && pos.x * scale > this._maxWidth) { - core.utils.removeItems(chars, lastSpace, i - lastSpace); + core.utils.removeItems(chars, lastSpace - spacesRemoved, i - lastSpace); i = lastSpace; lastSpace = -1; + ++spacesRemoved; lineWidths.push(lastSpaceWidth); maxLineWidth = Math.max(maxLineWidth, lastSpaceWidth); @@ -275,7 +279,7 @@ this._glyphs[i].y -= this._textHeight * this.anchor.y; } } - this.maxLineHeight = maxLineHeight * scale; + this._maxLineHeight = maxLineHeight * scale; } /** @@ -430,6 +434,42 @@ } /** + * The max width of this bitmap text in pixels. If the text provided is longer than the + * value provided, line breaks will be automatically inserted in the last whitespace. + * Disable by setting value to 0 + * + * @member {number} + */ + get maxWidth() + { + return this._maxWidth; + } + + set maxWidth(value) // eslint-disable-line require-jsdoc + { + if (this._maxWidth === value) + { + return; + } + this._maxWidth = value; + this.dirty = true; + } + + /** + * The max line height. This is useful when trying to use the total height of the Text, + * ie: when trying to vertically align. + * + * @member {number} + * @readonly + */ + get maxLineHeight() + { + this.validate(); + + return this._maxLineHeight; + } + + /** * The width of the overall text, different from fontSize, * which is defined in the style object * diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 72fee21..6282341 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -206,7 +206,8 @@ } else { - tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x, -texture._frame.y); + tempCanvas.context.drawImage(baseTexture.source, + -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } @@ -310,11 +311,11 @@ const height = this._height; const x1 = -width * this.anchor._x; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { const y1 = -height * this.anchor._y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 1340fcc..0592b54 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -96,16 +96,18 @@ * Disable by setting value to 0 * * @member {number} + * @private */ - this.maxWidth = 0; + this._maxWidth = 0; /** * The max line height. This is useful when trying to use the total height of the Text, * ie: when trying to vertically align. * * @member {number} + * @private */ - this.maxLineHeight = 0; + this._maxLineHeight = 0; /** * Text anchor. read-only @@ -144,6 +146,7 @@ let line = 0; let lastSpace = -1; let lastSpaceWidth = 0; + let spacesRemoved = 0; let maxLineHeight = 0; for (let i = 0; i < this.text.length; i++) @@ -168,11 +171,12 @@ continue; } - if (lastSpace !== -1 && this.maxWidth > 0 && pos.x * scale > this.maxWidth) + if (lastSpace !== -1 && this._maxWidth > 0 && pos.x * scale > this._maxWidth) { - core.utils.removeItems(chars, lastSpace, i - lastSpace); + core.utils.removeItems(chars, lastSpace - spacesRemoved, i - lastSpace); i = lastSpace; lastSpace = -1; + ++spacesRemoved; lineWidths.push(lastSpaceWidth); maxLineWidth = Math.max(maxLineWidth, lastSpaceWidth); @@ -275,7 +279,7 @@ this._glyphs[i].y -= this._textHeight * this.anchor.y; } } - this.maxLineHeight = maxLineHeight * scale; + this._maxLineHeight = maxLineHeight * scale; } /** @@ -430,6 +434,42 @@ } /** + * The max width of this bitmap text in pixels. If the text provided is longer than the + * value provided, line breaks will be automatically inserted in the last whitespace. + * Disable by setting value to 0 + * + * @member {number} + */ + get maxWidth() + { + return this._maxWidth; + } + + set maxWidth(value) // eslint-disable-line require-jsdoc + { + if (this._maxWidth === value) + { + return; + } + this._maxWidth = value; + this.dirty = true; + } + + /** + * The max line height. This is useful when trying to use the total height of the Text, + * ie: when trying to vertically align. + * + * @member {number} + * @readonly + */ + get maxLineHeight() + { + this.validate(); + + return this._maxLineHeight; + } + + /** * The width of the overall text, different from fontSize, * which is defined in the style object * diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 72fee21..6282341 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -206,7 +206,8 @@ } else { - tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x, -texture._frame.y); + tempCanvas.context.drawImage(baseTexture.source, + -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } @@ -310,11 +311,11 @@ const height = this._height; const x1 = -width * this.anchor._x; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { const y1 = -height * this.anchor._y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 5734fc0..49c3a29 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -4,7 +4,6 @@ import { join } from 'path'; const tempMat = new core.Matrix(); -const tempArray = new Float32Array(4); /** * WebGL renderer plugin for tiling sprites @@ -141,17 +140,13 @@ } shader.uniforms.uTransform = tempMat.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(ts.tint, color); - color[3] = ts.worldAlpha; - shader.uniforms.uColor = color; + shader.uniforms.uColor = core.utils.premultiplyTintToRgba(ts.tint, ts.worldAlpha, + shader.uniforms.uColor, baseTex.premultipliedAlpha); shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); shader.uniforms.uSampler = renderer.bindTexture(tex); - renderer.setBlendMode(ts.blendMode); + renderer.setBlendMode(core.utils.correctBlendMode(ts.blendMode, baseTex.premultipliedAlpha)); quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); } diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 1340fcc..0592b54 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -96,16 +96,18 @@ * Disable by setting value to 0 * * @member {number} + * @private */ - this.maxWidth = 0; + this._maxWidth = 0; /** * The max line height. This is useful when trying to use the total height of the Text, * ie: when trying to vertically align. * * @member {number} + * @private */ - this.maxLineHeight = 0; + this._maxLineHeight = 0; /** * Text anchor. read-only @@ -144,6 +146,7 @@ let line = 0; let lastSpace = -1; let lastSpaceWidth = 0; + let spacesRemoved = 0; let maxLineHeight = 0; for (let i = 0; i < this.text.length; i++) @@ -168,11 +171,12 @@ continue; } - if (lastSpace !== -1 && this.maxWidth > 0 && pos.x * scale > this.maxWidth) + if (lastSpace !== -1 && this._maxWidth > 0 && pos.x * scale > this._maxWidth) { - core.utils.removeItems(chars, lastSpace, i - lastSpace); + core.utils.removeItems(chars, lastSpace - spacesRemoved, i - lastSpace); i = lastSpace; lastSpace = -1; + ++spacesRemoved; lineWidths.push(lastSpaceWidth); maxLineWidth = Math.max(maxLineWidth, lastSpaceWidth); @@ -275,7 +279,7 @@ this._glyphs[i].y -= this._textHeight * this.anchor.y; } } - this.maxLineHeight = maxLineHeight * scale; + this._maxLineHeight = maxLineHeight * scale; } /** @@ -430,6 +434,42 @@ } /** + * The max width of this bitmap text in pixels. If the text provided is longer than the + * value provided, line breaks will be automatically inserted in the last whitespace. + * Disable by setting value to 0 + * + * @member {number} + */ + get maxWidth() + { + return this._maxWidth; + } + + set maxWidth(value) // eslint-disable-line require-jsdoc + { + if (this._maxWidth === value) + { + return; + } + this._maxWidth = value; + this.dirty = true; + } + + /** + * The max line height. This is useful when trying to use the total height of the Text, + * ie: when trying to vertically align. + * + * @member {number} + * @readonly + */ + get maxLineHeight() + { + this.validate(); + + return this._maxLineHeight; + } + + /** * The width of the overall text, different from fontSize, * which is defined in the style object * diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 72fee21..6282341 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -206,7 +206,8 @@ } else { - tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x, -texture._frame.y); + tempCanvas.context.drawImage(baseTexture.source, + -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } @@ -310,11 +311,11 @@ const height = this._height; const x1 = -width * this.anchor._x; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { const y1 = -height * this.anchor._y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 5734fc0..49c3a29 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -4,7 +4,6 @@ import { join } from 'path'; const tempMat = new core.Matrix(); -const tempArray = new Float32Array(4); /** * WebGL renderer plugin for tiling sprites @@ -141,17 +140,13 @@ } shader.uniforms.uTransform = tempMat.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(ts.tint, color); - color[3] = ts.worldAlpha; - shader.uniforms.uColor = color; + shader.uniforms.uColor = core.utils.premultiplyTintToRgba(ts.tint, ts.worldAlpha, + shader.uniforms.uColor, baseTex.premultipliedAlpha); shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); shader.uniforms.uSampler = renderer.bindTexture(tex); - renderer.setBlendMode(ts.blendMode); + renderer.setBlendMode(core.utils.correctBlendMode(ts.blendMode, baseTex.premultipliedAlpha)); quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); } diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 9d59adb..d1f599f 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -104,7 +104,7 @@ glData.shader.uniforms.uSampler = renderer.bindTexture(texture); - renderer.state.setBlendMode(mesh.blendMode); + renderer.state.setBlendMode(core.utils.correctBlendMode(mesh.blendMode, texture.baseTexture.premultipliedAlpha)); if (glData.shader.uniforms.uTransform) { @@ -118,8 +118,9 @@ } } glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); - glData.shader.uniforms.alpha = mesh.worldAlpha; - glData.shader.uniforms.tint = mesh.tintRgb; + + glData.shader.uniforms.uColor = core.utils.premultiplyRgba(mesh.tintRgb, + mesh.worldAlpha, glData.shader.uniforms.uColor, texture.baseTexture.premultipliedAlpha); const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 1340fcc..0592b54 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -96,16 +96,18 @@ * Disable by setting value to 0 * * @member {number} + * @private */ - this.maxWidth = 0; + this._maxWidth = 0; /** * The max line height. This is useful when trying to use the total height of the Text, * ie: when trying to vertically align. * * @member {number} + * @private */ - this.maxLineHeight = 0; + this._maxLineHeight = 0; /** * Text anchor. read-only @@ -144,6 +146,7 @@ let line = 0; let lastSpace = -1; let lastSpaceWidth = 0; + let spacesRemoved = 0; let maxLineHeight = 0; for (let i = 0; i < this.text.length; i++) @@ -168,11 +171,12 @@ continue; } - if (lastSpace !== -1 && this.maxWidth > 0 && pos.x * scale > this.maxWidth) + if (lastSpace !== -1 && this._maxWidth > 0 && pos.x * scale > this._maxWidth) { - core.utils.removeItems(chars, lastSpace, i - lastSpace); + core.utils.removeItems(chars, lastSpace - spacesRemoved, i - lastSpace); i = lastSpace; lastSpace = -1; + ++spacesRemoved; lineWidths.push(lastSpaceWidth); maxLineWidth = Math.max(maxLineWidth, lastSpaceWidth); @@ -275,7 +279,7 @@ this._glyphs[i].y -= this._textHeight * this.anchor.y; } } - this.maxLineHeight = maxLineHeight * scale; + this._maxLineHeight = maxLineHeight * scale; } /** @@ -430,6 +434,42 @@ } /** + * The max width of this bitmap text in pixels. If the text provided is longer than the + * value provided, line breaks will be automatically inserted in the last whitespace. + * Disable by setting value to 0 + * + * @member {number} + */ + get maxWidth() + { + return this._maxWidth; + } + + set maxWidth(value) // eslint-disable-line require-jsdoc + { + if (this._maxWidth === value) + { + return; + } + this._maxWidth = value; + this.dirty = true; + } + + /** + * The max line height. This is useful when trying to use the total height of the Text, + * ie: when trying to vertically align. + * + * @member {number} + * @readonly + */ + get maxLineHeight() + { + this.validate(); + + return this._maxLineHeight; + } + + /** * The width of the overall text, different from fontSize, * which is defined in the style object * diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 72fee21..6282341 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -206,7 +206,8 @@ } else { - tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x, -texture._frame.y); + tempCanvas.context.drawImage(baseTexture.source, + -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } @@ -310,11 +311,11 @@ const height = this._height; const x1 = -width * this.anchor._x; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { const y1 = -height * this.anchor._y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 5734fc0..49c3a29 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -4,7 +4,6 @@ import { join } from 'path'; const tempMat = new core.Matrix(); -const tempArray = new Float32Array(4); /** * WebGL renderer plugin for tiling sprites @@ -141,17 +140,13 @@ } shader.uniforms.uTransform = tempMat.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(ts.tint, color); - color[3] = ts.worldAlpha; - shader.uniforms.uColor = color; + shader.uniforms.uColor = core.utils.premultiplyTintToRgba(ts.tint, ts.worldAlpha, + shader.uniforms.uColor, baseTex.premultipliedAlpha); shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); shader.uniforms.uSampler = renderer.bindTexture(tex); - renderer.setBlendMode(ts.blendMode); + renderer.setBlendMode(core.utils.correctBlendMode(ts.blendMode, baseTex.premultipliedAlpha)); quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); } diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 9d59adb..d1f599f 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -104,7 +104,7 @@ glData.shader.uniforms.uSampler = renderer.bindTexture(texture); - renderer.state.setBlendMode(mesh.blendMode); + renderer.state.setBlendMode(core.utils.correctBlendMode(mesh.blendMode, texture.baseTexture.premultipliedAlpha)); if (glData.shader.uniforms.uTransform) { @@ -118,8 +118,9 @@ } } glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); - glData.shader.uniforms.alpha = mesh.worldAlpha; - glData.shader.uniforms.tint = mesh.tintRgb; + + glData.shader.uniforms.uColor = core.utils.premultiplyRgba(mesh.tintRgb, + mesh.worldAlpha, glData.shader.uniforms.uColor, texture.baseTexture.premultipliedAlpha); const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag index 9e0b634..6096983 100644 --- a/src/mesh/webgl/mesh.frag +++ b/src/mesh/webgl/mesh.frag @@ -1,10 +1,9 @@ varying vec2 vTextureCoord; -uniform float alpha; -uniform vec3 tint; +uniform vec4 uColor; uniform sampler2D uSampler; void main(void) { - gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); + gl_FragColor = texture2D(uSampler, vTextureCoord) * uColor; } diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 1340fcc..0592b54 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -96,16 +96,18 @@ * Disable by setting value to 0 * * @member {number} + * @private */ - this.maxWidth = 0; + this._maxWidth = 0; /** * The max line height. This is useful when trying to use the total height of the Text, * ie: when trying to vertically align. * * @member {number} + * @private */ - this.maxLineHeight = 0; + this._maxLineHeight = 0; /** * Text anchor. read-only @@ -144,6 +146,7 @@ let line = 0; let lastSpace = -1; let lastSpaceWidth = 0; + let spacesRemoved = 0; let maxLineHeight = 0; for (let i = 0; i < this.text.length; i++) @@ -168,11 +171,12 @@ continue; } - if (lastSpace !== -1 && this.maxWidth > 0 && pos.x * scale > this.maxWidth) + if (lastSpace !== -1 && this._maxWidth > 0 && pos.x * scale > this._maxWidth) { - core.utils.removeItems(chars, lastSpace, i - lastSpace); + core.utils.removeItems(chars, lastSpace - spacesRemoved, i - lastSpace); i = lastSpace; lastSpace = -1; + ++spacesRemoved; lineWidths.push(lastSpaceWidth); maxLineWidth = Math.max(maxLineWidth, lastSpaceWidth); @@ -275,7 +279,7 @@ this._glyphs[i].y -= this._textHeight * this.anchor.y; } } - this.maxLineHeight = maxLineHeight * scale; + this._maxLineHeight = maxLineHeight * scale; } /** @@ -430,6 +434,42 @@ } /** + * The max width of this bitmap text in pixels. If the text provided is longer than the + * value provided, line breaks will be automatically inserted in the last whitespace. + * Disable by setting value to 0 + * + * @member {number} + */ + get maxWidth() + { + return this._maxWidth; + } + + set maxWidth(value) // eslint-disable-line require-jsdoc + { + if (this._maxWidth === value) + { + return; + } + this._maxWidth = value; + this.dirty = true; + } + + /** + * The max line height. This is useful when trying to use the total height of the Text, + * ie: when trying to vertically align. + * + * @member {number} + * @readonly + */ + get maxLineHeight() + { + this.validate(); + + return this._maxLineHeight; + } + + /** * The width of the overall text, different from fontSize, * which is defined in the style object * diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 72fee21..6282341 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -206,7 +206,8 @@ } else { - tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x, -texture._frame.y); + tempCanvas.context.drawImage(baseTexture.source, + -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } @@ -310,11 +311,11 @@ const height = this._height; const x1 = -width * this.anchor._x; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { const y1 = -height * this.anchor._y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 5734fc0..49c3a29 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -4,7 +4,6 @@ import { join } from 'path'; const tempMat = new core.Matrix(); -const tempArray = new Float32Array(4); /** * WebGL renderer plugin for tiling sprites @@ -141,17 +140,13 @@ } shader.uniforms.uTransform = tempMat.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(ts.tint, color); - color[3] = ts.worldAlpha; - shader.uniforms.uColor = color; + shader.uniforms.uColor = core.utils.premultiplyTintToRgba(ts.tint, ts.worldAlpha, + shader.uniforms.uColor, baseTex.premultipliedAlpha); shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); shader.uniforms.uSampler = renderer.bindTexture(tex); - renderer.setBlendMode(ts.blendMode); + renderer.setBlendMode(core.utils.correctBlendMode(ts.blendMode, baseTex.premultipliedAlpha)); quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); } diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 9d59adb..d1f599f 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -104,7 +104,7 @@ glData.shader.uniforms.uSampler = renderer.bindTexture(texture); - renderer.state.setBlendMode(mesh.blendMode); + renderer.state.setBlendMode(core.utils.correctBlendMode(mesh.blendMode, texture.baseTexture.premultipliedAlpha)); if (glData.shader.uniforms.uTransform) { @@ -118,8 +118,9 @@ } } glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); - glData.shader.uniforms.alpha = mesh.worldAlpha; - glData.shader.uniforms.tint = mesh.tintRgb; + + glData.shader.uniforms.uColor = core.utils.premultiplyRgba(mesh.tintRgb, + mesh.worldAlpha, glData.shader.uniforms.uColor, texture.baseTexture.premultipliedAlpha); const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag index 9e0b634..6096983 100644 --- a/src/mesh/webgl/mesh.frag +++ b/src/mesh/webgl/mesh.frag @@ -1,10 +1,9 @@ varying vec2 vTextureCoord; -uniform float alpha; -uniform vec3 tint; +uniform vec4 uColor; uniform sampler2D uSampler; void main(void) { - gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); + gl_FragColor = texture2D(uSampler, vTextureCoord) * uColor; } diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index 6d98098..d6dc59d 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -131,8 +131,8 @@ * @member {number} * @default 0xFFFFFF */ - this._tint = null; - this._tintRGB = []; + this._tint = 0; + this.tintRgb = new Float32Array(4); this.tint = 0xFFFFFF; } @@ -180,7 +180,7 @@ set tint(value) // eslint-disable-line require-jsdoc { this._tint = value; - hex2rgb(value, this._tintRGB); + hex2rgb(value, this.tintRgb); } /** diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 1340fcc..0592b54 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -96,16 +96,18 @@ * Disable by setting value to 0 * * @member {number} + * @private */ - this.maxWidth = 0; + this._maxWidth = 0; /** * The max line height. This is useful when trying to use the total height of the Text, * ie: when trying to vertically align. * * @member {number} + * @private */ - this.maxLineHeight = 0; + this._maxLineHeight = 0; /** * Text anchor. read-only @@ -144,6 +146,7 @@ let line = 0; let lastSpace = -1; let lastSpaceWidth = 0; + let spacesRemoved = 0; let maxLineHeight = 0; for (let i = 0; i < this.text.length; i++) @@ -168,11 +171,12 @@ continue; } - if (lastSpace !== -1 && this.maxWidth > 0 && pos.x * scale > this.maxWidth) + if (lastSpace !== -1 && this._maxWidth > 0 && pos.x * scale > this._maxWidth) { - core.utils.removeItems(chars, lastSpace, i - lastSpace); + core.utils.removeItems(chars, lastSpace - spacesRemoved, i - lastSpace); i = lastSpace; lastSpace = -1; + ++spacesRemoved; lineWidths.push(lastSpaceWidth); maxLineWidth = Math.max(maxLineWidth, lastSpaceWidth); @@ -275,7 +279,7 @@ this._glyphs[i].y -= this._textHeight * this.anchor.y; } } - this.maxLineHeight = maxLineHeight * scale; + this._maxLineHeight = maxLineHeight * scale; } /** @@ -430,6 +434,42 @@ } /** + * The max width of this bitmap text in pixels. If the text provided is longer than the + * value provided, line breaks will be automatically inserted in the last whitespace. + * Disable by setting value to 0 + * + * @member {number} + */ + get maxWidth() + { + return this._maxWidth; + } + + set maxWidth(value) // eslint-disable-line require-jsdoc + { + if (this._maxWidth === value) + { + return; + } + this._maxWidth = value; + this.dirty = true; + } + + /** + * The max line height. This is useful when trying to use the total height of the Text, + * ie: when trying to vertically align. + * + * @member {number} + * @readonly + */ + get maxLineHeight() + { + this.validate(); + + return this._maxLineHeight; + } + + /** * The width of the overall text, different from fontSize, * which is defined in the style object * diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 72fee21..6282341 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -206,7 +206,8 @@ } else { - tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x, -texture._frame.y); + tempCanvas.context.drawImage(baseTexture.source, + -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } @@ -310,11 +311,11 @@ const height = this._height; const x1 = -width * this.anchor._x; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { const y1 = -height * this.anchor._y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 5734fc0..49c3a29 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -4,7 +4,6 @@ import { join } from 'path'; const tempMat = new core.Matrix(); -const tempArray = new Float32Array(4); /** * WebGL renderer plugin for tiling sprites @@ -141,17 +140,13 @@ } shader.uniforms.uTransform = tempMat.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(ts.tint, color); - color[3] = ts.worldAlpha; - shader.uniforms.uColor = color; + shader.uniforms.uColor = core.utils.premultiplyTintToRgba(ts.tint, ts.worldAlpha, + shader.uniforms.uColor, baseTex.premultipliedAlpha); shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); shader.uniforms.uSampler = renderer.bindTexture(tex); - renderer.setBlendMode(ts.blendMode); + renderer.setBlendMode(core.utils.correctBlendMode(ts.blendMode, baseTex.premultipliedAlpha)); quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); } diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 9d59adb..d1f599f 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -104,7 +104,7 @@ glData.shader.uniforms.uSampler = renderer.bindTexture(texture); - renderer.state.setBlendMode(mesh.blendMode); + renderer.state.setBlendMode(core.utils.correctBlendMode(mesh.blendMode, texture.baseTexture.premultipliedAlpha)); if (glData.shader.uniforms.uTransform) { @@ -118,8 +118,9 @@ } } glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); - glData.shader.uniforms.alpha = mesh.worldAlpha; - glData.shader.uniforms.tint = mesh.tintRgb; + + glData.shader.uniforms.uColor = core.utils.premultiplyRgba(mesh.tintRgb, + mesh.worldAlpha, glData.shader.uniforms.uColor, texture.baseTexture.premultipliedAlpha); const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag index 9e0b634..6096983 100644 --- a/src/mesh/webgl/mesh.frag +++ b/src/mesh/webgl/mesh.frag @@ -1,10 +1,9 @@ varying vec2 vTextureCoord; -uniform float alpha; -uniform vec3 tint; +uniform vec4 uColor; uniform sampler2D uSampler; void main(void) { - gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); + gl_FragColor = texture2D(uSampler, vTextureCoord) * uColor; } diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index 6d98098..d6dc59d 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -131,8 +131,8 @@ * @member {number} * @default 0xFFFFFF */ - this._tint = null; - this._tintRGB = []; + this._tint = 0; + this.tintRgb = new Float32Array(4); this.tint = 0xFFFFFF; } @@ -180,7 +180,7 @@ set tint(value) // eslint-disable-line require-jsdoc { this._tint = value; - hex2rgb(value, this._tintRGB); + hex2rgb(value, this.tintRgb); } /** diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js index 16e0c9c..aaf3638 100644 --- a/src/particles/webgl/ParticleRenderer.js +++ b/src/particles/webgl/ParticleRenderer.js @@ -142,8 +142,10 @@ buffers = container._glBuffers[renderer.CONTEXT_UID] = this.generateBuffers(container); } + const baseTexture = children[0]._texture.baseTexture; + // if the uvs have not updated then no point rendering just yet! - this.renderer.setBlendMode(container.blendMode); + this.renderer.setBlendMode(core.utils.correctBlendMode(container.blendMode, baseTexture.premultipliedAlpha)); const gl = renderer.gl; @@ -152,12 +154,11 @@ m.prepend(renderer._activeRenderTarget.projectionMatrix); this.shader.uniforms.projectionMatrix = m.toArray(true); - this.shader.uniforms.uAlpha = container.worldAlpha; - this.shader.uniforms.tint = container._tintRGB; + + this.shader.uniforms.uColor = core.utils.premultiplyRgba(container.tintRgb, + container.worldAlpha, this.shader.uniforms.uColor, baseTexture.premultipliedAlpha); // make sure the texture is bound.. - const baseTexture = children[0]._texture.baseTexture; - this.shader.uniforms.uSampler = renderer.bindTexture(baseTexture); // now lets upload and render the buffers.. diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 1340fcc..0592b54 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -96,16 +96,18 @@ * Disable by setting value to 0 * * @member {number} + * @private */ - this.maxWidth = 0; + this._maxWidth = 0; /** * The max line height. This is useful when trying to use the total height of the Text, * ie: when trying to vertically align. * * @member {number} + * @private */ - this.maxLineHeight = 0; + this._maxLineHeight = 0; /** * Text anchor. read-only @@ -144,6 +146,7 @@ let line = 0; let lastSpace = -1; let lastSpaceWidth = 0; + let spacesRemoved = 0; let maxLineHeight = 0; for (let i = 0; i < this.text.length; i++) @@ -168,11 +171,12 @@ continue; } - if (lastSpace !== -1 && this.maxWidth > 0 && pos.x * scale > this.maxWidth) + if (lastSpace !== -1 && this._maxWidth > 0 && pos.x * scale > this._maxWidth) { - core.utils.removeItems(chars, lastSpace, i - lastSpace); + core.utils.removeItems(chars, lastSpace - spacesRemoved, i - lastSpace); i = lastSpace; lastSpace = -1; + ++spacesRemoved; lineWidths.push(lastSpaceWidth); maxLineWidth = Math.max(maxLineWidth, lastSpaceWidth); @@ -275,7 +279,7 @@ this._glyphs[i].y -= this._textHeight * this.anchor.y; } } - this.maxLineHeight = maxLineHeight * scale; + this._maxLineHeight = maxLineHeight * scale; } /** @@ -430,6 +434,42 @@ } /** + * The max width of this bitmap text in pixels. If the text provided is longer than the + * value provided, line breaks will be automatically inserted in the last whitespace. + * Disable by setting value to 0 + * + * @member {number} + */ + get maxWidth() + { + return this._maxWidth; + } + + set maxWidth(value) // eslint-disable-line require-jsdoc + { + if (this._maxWidth === value) + { + return; + } + this._maxWidth = value; + this.dirty = true; + } + + /** + * The max line height. This is useful when trying to use the total height of the Text, + * ie: when trying to vertically align. + * + * @member {number} + * @readonly + */ + get maxLineHeight() + { + this.validate(); + + return this._maxLineHeight; + } + + /** * The width of the overall text, different from fontSize, * which is defined in the style object * diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 72fee21..6282341 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -206,7 +206,8 @@ } else { - tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x, -texture._frame.y); + tempCanvas.context.drawImage(baseTexture.source, + -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } @@ -310,11 +311,11 @@ const height = this._height; const x1 = -width * this.anchor._x; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { const y1 = -height * this.anchor._y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 5734fc0..49c3a29 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -4,7 +4,6 @@ import { join } from 'path'; const tempMat = new core.Matrix(); -const tempArray = new Float32Array(4); /** * WebGL renderer plugin for tiling sprites @@ -141,17 +140,13 @@ } shader.uniforms.uTransform = tempMat.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(ts.tint, color); - color[3] = ts.worldAlpha; - shader.uniforms.uColor = color; + shader.uniforms.uColor = core.utils.premultiplyTintToRgba(ts.tint, ts.worldAlpha, + shader.uniforms.uColor, baseTex.premultipliedAlpha); shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); shader.uniforms.uSampler = renderer.bindTexture(tex); - renderer.setBlendMode(ts.blendMode); + renderer.setBlendMode(core.utils.correctBlendMode(ts.blendMode, baseTex.premultipliedAlpha)); quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); } diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 9d59adb..d1f599f 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -104,7 +104,7 @@ glData.shader.uniforms.uSampler = renderer.bindTexture(texture); - renderer.state.setBlendMode(mesh.blendMode); + renderer.state.setBlendMode(core.utils.correctBlendMode(mesh.blendMode, texture.baseTexture.premultipliedAlpha)); if (glData.shader.uniforms.uTransform) { @@ -118,8 +118,9 @@ } } glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); - glData.shader.uniforms.alpha = mesh.worldAlpha; - glData.shader.uniforms.tint = mesh.tintRgb; + + glData.shader.uniforms.uColor = core.utils.premultiplyRgba(mesh.tintRgb, + mesh.worldAlpha, glData.shader.uniforms.uColor, texture.baseTexture.premultipliedAlpha); const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag index 9e0b634..6096983 100644 --- a/src/mesh/webgl/mesh.frag +++ b/src/mesh/webgl/mesh.frag @@ -1,10 +1,9 @@ varying vec2 vTextureCoord; -uniform float alpha; -uniform vec3 tint; +uniform vec4 uColor; uniform sampler2D uSampler; void main(void) { - gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); + gl_FragColor = texture2D(uSampler, vTextureCoord) * uColor; } diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index 6d98098..d6dc59d 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -131,8 +131,8 @@ * @member {number} * @default 0xFFFFFF */ - this._tint = null; - this._tintRGB = []; + this._tint = 0; + this.tintRgb = new Float32Array(4); this.tint = 0xFFFFFF; } @@ -180,7 +180,7 @@ set tint(value) // eslint-disable-line require-jsdoc { this._tint = value; - hex2rgb(value, this._tintRGB); + hex2rgb(value, this.tintRgb); } /** diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js index 16e0c9c..aaf3638 100644 --- a/src/particles/webgl/ParticleRenderer.js +++ b/src/particles/webgl/ParticleRenderer.js @@ -142,8 +142,10 @@ buffers = container._glBuffers[renderer.CONTEXT_UID] = this.generateBuffers(container); } + const baseTexture = children[0]._texture.baseTexture; + // if the uvs have not updated then no point rendering just yet! - this.renderer.setBlendMode(container.blendMode); + this.renderer.setBlendMode(core.utils.correctBlendMode(container.blendMode, baseTexture.premultipliedAlpha)); const gl = renderer.gl; @@ -152,12 +154,11 @@ m.prepend(renderer._activeRenderTarget.projectionMatrix); this.shader.uniforms.projectionMatrix = m.toArray(true); - this.shader.uniforms.uAlpha = container.worldAlpha; - this.shader.uniforms.tint = container._tintRGB; + + this.shader.uniforms.uColor = core.utils.premultiplyRgba(container.tintRgb, + container.worldAlpha, this.shader.uniforms.uColor, baseTexture.premultipliedAlpha); // make sure the texture is bound.. - const baseTexture = children[0]._texture.baseTexture; - this.shader.uniforms.uSampler = renderer.bindTexture(baseTexture); // now lets upload and render the buffers.. diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js index ebd80d9..4361623 100644 --- a/src/particles/webgl/ParticleShader.js +++ b/src/particles/webgl/ParticleShader.js @@ -48,11 +48,10 @@ 'varying float vColor;', 'uniform sampler2D uSampler;', - 'uniform float uAlpha;', - 'uniform vec3 tint;', + 'uniform vec4 uColor;', 'void main(void){', - ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);', + ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uColor;', ' if (color.a == 0.0) discard;', ' gl_FragColor = color;', '}', diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 1340fcc..0592b54 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -96,16 +96,18 @@ * Disable by setting value to 0 * * @member {number} + * @private */ - this.maxWidth = 0; + this._maxWidth = 0; /** * The max line height. This is useful when trying to use the total height of the Text, * ie: when trying to vertically align. * * @member {number} + * @private */ - this.maxLineHeight = 0; + this._maxLineHeight = 0; /** * Text anchor. read-only @@ -144,6 +146,7 @@ let line = 0; let lastSpace = -1; let lastSpaceWidth = 0; + let spacesRemoved = 0; let maxLineHeight = 0; for (let i = 0; i < this.text.length; i++) @@ -168,11 +171,12 @@ continue; } - if (lastSpace !== -1 && this.maxWidth > 0 && pos.x * scale > this.maxWidth) + if (lastSpace !== -1 && this._maxWidth > 0 && pos.x * scale > this._maxWidth) { - core.utils.removeItems(chars, lastSpace, i - lastSpace); + core.utils.removeItems(chars, lastSpace - spacesRemoved, i - lastSpace); i = lastSpace; lastSpace = -1; + ++spacesRemoved; lineWidths.push(lastSpaceWidth); maxLineWidth = Math.max(maxLineWidth, lastSpaceWidth); @@ -275,7 +279,7 @@ this._glyphs[i].y -= this._textHeight * this.anchor.y; } } - this.maxLineHeight = maxLineHeight * scale; + this._maxLineHeight = maxLineHeight * scale; } /** @@ -430,6 +434,42 @@ } /** + * The max width of this bitmap text in pixels. If the text provided is longer than the + * value provided, line breaks will be automatically inserted in the last whitespace. + * Disable by setting value to 0 + * + * @member {number} + */ + get maxWidth() + { + return this._maxWidth; + } + + set maxWidth(value) // eslint-disable-line require-jsdoc + { + if (this._maxWidth === value) + { + return; + } + this._maxWidth = value; + this.dirty = true; + } + + /** + * The max line height. This is useful when trying to use the total height of the Text, + * ie: when trying to vertically align. + * + * @member {number} + * @readonly + */ + get maxLineHeight() + { + this.validate(); + + return this._maxLineHeight; + } + + /** * The width of the overall text, different from fontSize, * which is defined in the style object * diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 72fee21..6282341 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -206,7 +206,8 @@ } else { - tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x, -texture._frame.y); + tempCanvas.context.drawImage(baseTexture.source, + -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } @@ -310,11 +311,11 @@ const height = this._height; const x1 = -width * this.anchor._x; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { const y1 = -height * this.anchor._y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 5734fc0..49c3a29 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -4,7 +4,6 @@ import { join } from 'path'; const tempMat = new core.Matrix(); -const tempArray = new Float32Array(4); /** * WebGL renderer plugin for tiling sprites @@ -141,17 +140,13 @@ } shader.uniforms.uTransform = tempMat.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(ts.tint, color); - color[3] = ts.worldAlpha; - shader.uniforms.uColor = color; + shader.uniforms.uColor = core.utils.premultiplyTintToRgba(ts.tint, ts.worldAlpha, + shader.uniforms.uColor, baseTex.premultipliedAlpha); shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); shader.uniforms.uSampler = renderer.bindTexture(tex); - renderer.setBlendMode(ts.blendMode); + renderer.setBlendMode(core.utils.correctBlendMode(ts.blendMode, baseTex.premultipliedAlpha)); quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); } diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 9d59adb..d1f599f 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -104,7 +104,7 @@ glData.shader.uniforms.uSampler = renderer.bindTexture(texture); - renderer.state.setBlendMode(mesh.blendMode); + renderer.state.setBlendMode(core.utils.correctBlendMode(mesh.blendMode, texture.baseTexture.premultipliedAlpha)); if (glData.shader.uniforms.uTransform) { @@ -118,8 +118,9 @@ } } glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); - glData.shader.uniforms.alpha = mesh.worldAlpha; - glData.shader.uniforms.tint = mesh.tintRgb; + + glData.shader.uniforms.uColor = core.utils.premultiplyRgba(mesh.tintRgb, + mesh.worldAlpha, glData.shader.uniforms.uColor, texture.baseTexture.premultipliedAlpha); const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag index 9e0b634..6096983 100644 --- a/src/mesh/webgl/mesh.frag +++ b/src/mesh/webgl/mesh.frag @@ -1,10 +1,9 @@ varying vec2 vTextureCoord; -uniform float alpha; -uniform vec3 tint; +uniform vec4 uColor; uniform sampler2D uSampler; void main(void) { - gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); + gl_FragColor = texture2D(uSampler, vTextureCoord) * uColor; } diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index 6d98098..d6dc59d 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -131,8 +131,8 @@ * @member {number} * @default 0xFFFFFF */ - this._tint = null; - this._tintRGB = []; + this._tint = 0; + this.tintRgb = new Float32Array(4); this.tint = 0xFFFFFF; } @@ -180,7 +180,7 @@ set tint(value) // eslint-disable-line require-jsdoc { this._tint = value; - hex2rgb(value, this._tintRGB); + hex2rgb(value, this.tintRgb); } /** diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js index 16e0c9c..aaf3638 100644 --- a/src/particles/webgl/ParticleRenderer.js +++ b/src/particles/webgl/ParticleRenderer.js @@ -142,8 +142,10 @@ buffers = container._glBuffers[renderer.CONTEXT_UID] = this.generateBuffers(container); } + const baseTexture = children[0]._texture.baseTexture; + // if the uvs have not updated then no point rendering just yet! - this.renderer.setBlendMode(container.blendMode); + this.renderer.setBlendMode(core.utils.correctBlendMode(container.blendMode, baseTexture.premultipliedAlpha)); const gl = renderer.gl; @@ -152,12 +154,11 @@ m.prepend(renderer._activeRenderTarget.projectionMatrix); this.shader.uniforms.projectionMatrix = m.toArray(true); - this.shader.uniforms.uAlpha = container.worldAlpha; - this.shader.uniforms.tint = container._tintRGB; + + this.shader.uniforms.uColor = core.utils.premultiplyRgba(container.tintRgb, + container.worldAlpha, this.shader.uniforms.uColor, baseTexture.premultipliedAlpha); // make sure the texture is bound.. - const baseTexture = children[0]._texture.baseTexture; - this.shader.uniforms.uSampler = renderer.bindTexture(baseTexture); // now lets upload and render the buffers.. diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js index ebd80d9..4361623 100644 --- a/src/particles/webgl/ParticleShader.js +++ b/src/particles/webgl/ParticleShader.js @@ -48,11 +48,10 @@ 'varying float vColor;', 'uniform sampler2D uSampler;', - 'uniform float uAlpha;', - 'uniform vec3 tint;', + 'uniform vec4 uColor;', 'void main(void){', - ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);', + ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uColor;', ' if (color.a == 0.0) discard;', ' gl_FragColor = color;', '}', diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js index e99de40..703175d 100644 --- a/test/core/BaseTexture.js +++ b/test/core/BaseTexture.js @@ -102,11 +102,20 @@ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); }); - it('destroying a destroyed BaseTexture should not throw an error', function () + it('should not throw an error destroying a destroyed BaseTexture', function () { const baseTexture = new PIXI.BaseTexture(); baseTexture.destroy(); baseTexture.destroy(); }); + + it('should set source.crossOrigin to anonymous if explicitly set', function () + { + cleanCache(); + + const baseTexture = PIXI.BaseTexture.fromImage(URL, true); + + expect(baseTexture.source.crossOrigin).to.equal('anonymous'); + }); }); diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 1340fcc..0592b54 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -96,16 +96,18 @@ * Disable by setting value to 0 * * @member {number} + * @private */ - this.maxWidth = 0; + this._maxWidth = 0; /** * The max line height. This is useful when trying to use the total height of the Text, * ie: when trying to vertically align. * * @member {number} + * @private */ - this.maxLineHeight = 0; + this._maxLineHeight = 0; /** * Text anchor. read-only @@ -144,6 +146,7 @@ let line = 0; let lastSpace = -1; let lastSpaceWidth = 0; + let spacesRemoved = 0; let maxLineHeight = 0; for (let i = 0; i < this.text.length; i++) @@ -168,11 +171,12 @@ continue; } - if (lastSpace !== -1 && this.maxWidth > 0 && pos.x * scale > this.maxWidth) + if (lastSpace !== -1 && this._maxWidth > 0 && pos.x * scale > this._maxWidth) { - core.utils.removeItems(chars, lastSpace, i - lastSpace); + core.utils.removeItems(chars, lastSpace - spacesRemoved, i - lastSpace); i = lastSpace; lastSpace = -1; + ++spacesRemoved; lineWidths.push(lastSpaceWidth); maxLineWidth = Math.max(maxLineWidth, lastSpaceWidth); @@ -275,7 +279,7 @@ this._glyphs[i].y -= this._textHeight * this.anchor.y; } } - this.maxLineHeight = maxLineHeight * scale; + this._maxLineHeight = maxLineHeight * scale; } /** @@ -430,6 +434,42 @@ } /** + * The max width of this bitmap text in pixels. If the text provided is longer than the + * value provided, line breaks will be automatically inserted in the last whitespace. + * Disable by setting value to 0 + * + * @member {number} + */ + get maxWidth() + { + return this._maxWidth; + } + + set maxWidth(value) // eslint-disable-line require-jsdoc + { + if (this._maxWidth === value) + { + return; + } + this._maxWidth = value; + this.dirty = true; + } + + /** + * The max line height. This is useful when trying to use the total height of the Text, + * ie: when trying to vertically align. + * + * @member {number} + * @readonly + */ + get maxLineHeight() + { + this.validate(); + + return this._maxLineHeight; + } + + /** * The width of the overall text, different from fontSize, * which is defined in the style object * diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 72fee21..6282341 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -206,7 +206,8 @@ } else { - tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x, -texture._frame.y); + tempCanvas.context.drawImage(baseTexture.source, + -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } @@ -310,11 +311,11 @@ const height = this._height; const x1 = -width * this.anchor._x; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { const y1 = -height * this.anchor._y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 5734fc0..49c3a29 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -4,7 +4,6 @@ import { join } from 'path'; const tempMat = new core.Matrix(); -const tempArray = new Float32Array(4); /** * WebGL renderer plugin for tiling sprites @@ -141,17 +140,13 @@ } shader.uniforms.uTransform = tempMat.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(ts.tint, color); - color[3] = ts.worldAlpha; - shader.uniforms.uColor = color; + shader.uniforms.uColor = core.utils.premultiplyTintToRgba(ts.tint, ts.worldAlpha, + shader.uniforms.uColor, baseTex.premultipliedAlpha); shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); shader.uniforms.uSampler = renderer.bindTexture(tex); - renderer.setBlendMode(ts.blendMode); + renderer.setBlendMode(core.utils.correctBlendMode(ts.blendMode, baseTex.premultipliedAlpha)); quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); } diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 9d59adb..d1f599f 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -104,7 +104,7 @@ glData.shader.uniforms.uSampler = renderer.bindTexture(texture); - renderer.state.setBlendMode(mesh.blendMode); + renderer.state.setBlendMode(core.utils.correctBlendMode(mesh.blendMode, texture.baseTexture.premultipliedAlpha)); if (glData.shader.uniforms.uTransform) { @@ -118,8 +118,9 @@ } } glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); - glData.shader.uniforms.alpha = mesh.worldAlpha; - glData.shader.uniforms.tint = mesh.tintRgb; + + glData.shader.uniforms.uColor = core.utils.premultiplyRgba(mesh.tintRgb, + mesh.worldAlpha, glData.shader.uniforms.uColor, texture.baseTexture.premultipliedAlpha); const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag index 9e0b634..6096983 100644 --- a/src/mesh/webgl/mesh.frag +++ b/src/mesh/webgl/mesh.frag @@ -1,10 +1,9 @@ varying vec2 vTextureCoord; -uniform float alpha; -uniform vec3 tint; +uniform vec4 uColor; uniform sampler2D uSampler; void main(void) { - gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); + gl_FragColor = texture2D(uSampler, vTextureCoord) * uColor; } diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index 6d98098..d6dc59d 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -131,8 +131,8 @@ * @member {number} * @default 0xFFFFFF */ - this._tint = null; - this._tintRGB = []; + this._tint = 0; + this.tintRgb = new Float32Array(4); this.tint = 0xFFFFFF; } @@ -180,7 +180,7 @@ set tint(value) // eslint-disable-line require-jsdoc { this._tint = value; - hex2rgb(value, this._tintRGB); + hex2rgb(value, this.tintRgb); } /** diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js index 16e0c9c..aaf3638 100644 --- a/src/particles/webgl/ParticleRenderer.js +++ b/src/particles/webgl/ParticleRenderer.js @@ -142,8 +142,10 @@ buffers = container._glBuffers[renderer.CONTEXT_UID] = this.generateBuffers(container); } + const baseTexture = children[0]._texture.baseTexture; + // if the uvs have not updated then no point rendering just yet! - this.renderer.setBlendMode(container.blendMode); + this.renderer.setBlendMode(core.utils.correctBlendMode(container.blendMode, baseTexture.premultipliedAlpha)); const gl = renderer.gl; @@ -152,12 +154,11 @@ m.prepend(renderer._activeRenderTarget.projectionMatrix); this.shader.uniforms.projectionMatrix = m.toArray(true); - this.shader.uniforms.uAlpha = container.worldAlpha; - this.shader.uniforms.tint = container._tintRGB; + + this.shader.uniforms.uColor = core.utils.premultiplyRgba(container.tintRgb, + container.worldAlpha, this.shader.uniforms.uColor, baseTexture.premultipliedAlpha); // make sure the texture is bound.. - const baseTexture = children[0]._texture.baseTexture; - this.shader.uniforms.uSampler = renderer.bindTexture(baseTexture); // now lets upload and render the buffers.. diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js index ebd80d9..4361623 100644 --- a/src/particles/webgl/ParticleShader.js +++ b/src/particles/webgl/ParticleShader.js @@ -48,11 +48,10 @@ 'varying float vColor;', 'uniform sampler2D uSampler;', - 'uniform float uAlpha;', - 'uniform vec3 tint;', + 'uniform vec4 uColor;', 'void main(void){', - ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);', + ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uColor;', ' if (color.a == 0.0) discard;', ' gl_FragColor = color;', '}', diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js index e99de40..703175d 100644 --- a/test/core/BaseTexture.js +++ b/test/core/BaseTexture.js @@ -102,11 +102,20 @@ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); }); - it('destroying a destroyed BaseTexture should not throw an error', function () + it('should not throw an error destroying a destroyed BaseTexture', function () { const baseTexture = new PIXI.BaseTexture(); baseTexture.destroy(); baseTexture.destroy(); }); + + it('should set source.crossOrigin to anonymous if explicitly set', function () + { + cleanCache(); + + const baseTexture = PIXI.BaseTexture.fromImage(URL, true); + + expect(baseTexture.source.crossOrigin).to.equal('anonymous'); + }); }); diff --git a/test/core/CanvasRenderer.js b/test/core/CanvasRenderer.js new file mode 100644 index 0000000..7ebf633 --- /dev/null +++ b/test/core/CanvasRenderer.js @@ -0,0 +1,32 @@ +'use strict'; + +describe('PIXI.CanvasRenderer', function () +{ + it('should default context to rootContext', function () + { + const renderer = new PIXI.CanvasRenderer(1, 1); + + try + { + expect(renderer.context).to.equal(renderer.rootContext); + } + finally + { + renderer.destroy(); + } + }); + + it('should allow clear() to work despite no containers added to the renderer', function () + { + const renderer = new PIXI.CanvasRenderer(1, 1); + + try + { + renderer.clear(); + } + finally + { + renderer.destroy(); + } + }); +}); diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 1340fcc..0592b54 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -96,16 +96,18 @@ * Disable by setting value to 0 * * @member {number} + * @private */ - this.maxWidth = 0; + this._maxWidth = 0; /** * The max line height. This is useful when trying to use the total height of the Text, * ie: when trying to vertically align. * * @member {number} + * @private */ - this.maxLineHeight = 0; + this._maxLineHeight = 0; /** * Text anchor. read-only @@ -144,6 +146,7 @@ let line = 0; let lastSpace = -1; let lastSpaceWidth = 0; + let spacesRemoved = 0; let maxLineHeight = 0; for (let i = 0; i < this.text.length; i++) @@ -168,11 +171,12 @@ continue; } - if (lastSpace !== -1 && this.maxWidth > 0 && pos.x * scale > this.maxWidth) + if (lastSpace !== -1 && this._maxWidth > 0 && pos.x * scale > this._maxWidth) { - core.utils.removeItems(chars, lastSpace, i - lastSpace); + core.utils.removeItems(chars, lastSpace - spacesRemoved, i - lastSpace); i = lastSpace; lastSpace = -1; + ++spacesRemoved; lineWidths.push(lastSpaceWidth); maxLineWidth = Math.max(maxLineWidth, lastSpaceWidth); @@ -275,7 +279,7 @@ this._glyphs[i].y -= this._textHeight * this.anchor.y; } } - this.maxLineHeight = maxLineHeight * scale; + this._maxLineHeight = maxLineHeight * scale; } /** @@ -430,6 +434,42 @@ } /** + * The max width of this bitmap text in pixels. If the text provided is longer than the + * value provided, line breaks will be automatically inserted in the last whitespace. + * Disable by setting value to 0 + * + * @member {number} + */ + get maxWidth() + { + return this._maxWidth; + } + + set maxWidth(value) // eslint-disable-line require-jsdoc + { + if (this._maxWidth === value) + { + return; + } + this._maxWidth = value; + this.dirty = true; + } + + /** + * The max line height. This is useful when trying to use the total height of the Text, + * ie: when trying to vertically align. + * + * @member {number} + * @readonly + */ + get maxLineHeight() + { + this.validate(); + + return this._maxLineHeight; + } + + /** * The width of the overall text, different from fontSize, * which is defined in the style object * diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 72fee21..6282341 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -206,7 +206,8 @@ } else { - tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x, -texture._frame.y); + tempCanvas.context.drawImage(baseTexture.source, + -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } @@ -310,11 +311,11 @@ const height = this._height; const x1 = -width * this.anchor._x; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { const y1 = -height * this.anchor._y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 5734fc0..49c3a29 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -4,7 +4,6 @@ import { join } from 'path'; const tempMat = new core.Matrix(); -const tempArray = new Float32Array(4); /** * WebGL renderer plugin for tiling sprites @@ -141,17 +140,13 @@ } shader.uniforms.uTransform = tempMat.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(ts.tint, color); - color[3] = ts.worldAlpha; - shader.uniforms.uColor = color; + shader.uniforms.uColor = core.utils.premultiplyTintToRgba(ts.tint, ts.worldAlpha, + shader.uniforms.uColor, baseTex.premultipliedAlpha); shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); shader.uniforms.uSampler = renderer.bindTexture(tex); - renderer.setBlendMode(ts.blendMode); + renderer.setBlendMode(core.utils.correctBlendMode(ts.blendMode, baseTex.premultipliedAlpha)); quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); } diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 9d59adb..d1f599f 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -104,7 +104,7 @@ glData.shader.uniforms.uSampler = renderer.bindTexture(texture); - renderer.state.setBlendMode(mesh.blendMode); + renderer.state.setBlendMode(core.utils.correctBlendMode(mesh.blendMode, texture.baseTexture.premultipliedAlpha)); if (glData.shader.uniforms.uTransform) { @@ -118,8 +118,9 @@ } } glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); - glData.shader.uniforms.alpha = mesh.worldAlpha; - glData.shader.uniforms.tint = mesh.tintRgb; + + glData.shader.uniforms.uColor = core.utils.premultiplyRgba(mesh.tintRgb, + mesh.worldAlpha, glData.shader.uniforms.uColor, texture.baseTexture.premultipliedAlpha); const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag index 9e0b634..6096983 100644 --- a/src/mesh/webgl/mesh.frag +++ b/src/mesh/webgl/mesh.frag @@ -1,10 +1,9 @@ varying vec2 vTextureCoord; -uniform float alpha; -uniform vec3 tint; +uniform vec4 uColor; uniform sampler2D uSampler; void main(void) { - gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); + gl_FragColor = texture2D(uSampler, vTextureCoord) * uColor; } diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index 6d98098..d6dc59d 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -131,8 +131,8 @@ * @member {number} * @default 0xFFFFFF */ - this._tint = null; - this._tintRGB = []; + this._tint = 0; + this.tintRgb = new Float32Array(4); this.tint = 0xFFFFFF; } @@ -180,7 +180,7 @@ set tint(value) // eslint-disable-line require-jsdoc { this._tint = value; - hex2rgb(value, this._tintRGB); + hex2rgb(value, this.tintRgb); } /** diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js index 16e0c9c..aaf3638 100644 --- a/src/particles/webgl/ParticleRenderer.js +++ b/src/particles/webgl/ParticleRenderer.js @@ -142,8 +142,10 @@ buffers = container._glBuffers[renderer.CONTEXT_UID] = this.generateBuffers(container); } + const baseTexture = children[0]._texture.baseTexture; + // if the uvs have not updated then no point rendering just yet! - this.renderer.setBlendMode(container.blendMode); + this.renderer.setBlendMode(core.utils.correctBlendMode(container.blendMode, baseTexture.premultipliedAlpha)); const gl = renderer.gl; @@ -152,12 +154,11 @@ m.prepend(renderer._activeRenderTarget.projectionMatrix); this.shader.uniforms.projectionMatrix = m.toArray(true); - this.shader.uniforms.uAlpha = container.worldAlpha; - this.shader.uniforms.tint = container._tintRGB; + + this.shader.uniforms.uColor = core.utils.premultiplyRgba(container.tintRgb, + container.worldAlpha, this.shader.uniforms.uColor, baseTexture.premultipliedAlpha); // make sure the texture is bound.. - const baseTexture = children[0]._texture.baseTexture; - this.shader.uniforms.uSampler = renderer.bindTexture(baseTexture); // now lets upload and render the buffers.. diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js index ebd80d9..4361623 100644 --- a/src/particles/webgl/ParticleShader.js +++ b/src/particles/webgl/ParticleShader.js @@ -48,11 +48,10 @@ 'varying float vColor;', 'uniform sampler2D uSampler;', - 'uniform float uAlpha;', - 'uniform vec3 tint;', + 'uniform vec4 uColor;', 'void main(void){', - ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);', + ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uColor;', ' if (color.a == 0.0) discard;', ' gl_FragColor = color;', '}', diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js index e99de40..703175d 100644 --- a/test/core/BaseTexture.js +++ b/test/core/BaseTexture.js @@ -102,11 +102,20 @@ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); }); - it('destroying a destroyed BaseTexture should not throw an error', function () + it('should not throw an error destroying a destroyed BaseTexture', function () { const baseTexture = new PIXI.BaseTexture(); baseTexture.destroy(); baseTexture.destroy(); }); + + it('should set source.crossOrigin to anonymous if explicitly set', function () + { + cleanCache(); + + const baseTexture = PIXI.BaseTexture.fromImage(URL, true); + + expect(baseTexture.source.crossOrigin).to.equal('anonymous'); + }); }); diff --git a/test/core/CanvasRenderer.js b/test/core/CanvasRenderer.js new file mode 100644 index 0000000..7ebf633 --- /dev/null +++ b/test/core/CanvasRenderer.js @@ -0,0 +1,32 @@ +'use strict'; + +describe('PIXI.CanvasRenderer', function () +{ + it('should default context to rootContext', function () + { + const renderer = new PIXI.CanvasRenderer(1, 1); + + try + { + expect(renderer.context).to.equal(renderer.rootContext); + } + finally + { + renderer.destroy(); + } + }); + + it('should allow clear() to work despite no containers added to the renderer', function () + { + const renderer = new PIXI.CanvasRenderer(1, 1); + + try + { + renderer.clear(); + } + finally + { + renderer.destroy(); + } + }); +}); diff --git a/test/core/Rectangle.js b/test/core/Rectangle.js index 40ca3b0..edd5a99 100644 --- a/test/core/Rectangle.js +++ b/test/core/Rectangle.js @@ -135,7 +135,7 @@ it('should fit a rectangle', function () { - const rect1 = new PIXI.Rectangle(0, 0, -10, -10); + const rect1 = new PIXI.Rectangle(0, 0, 10, 10); const rect2 = new PIXI.Rectangle(-10, -10, 5, 5); rect2.fit(rect1); diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 1340fcc..0592b54 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -96,16 +96,18 @@ * Disable by setting value to 0 * * @member {number} + * @private */ - this.maxWidth = 0; + this._maxWidth = 0; /** * The max line height. This is useful when trying to use the total height of the Text, * ie: when trying to vertically align. * * @member {number} + * @private */ - this.maxLineHeight = 0; + this._maxLineHeight = 0; /** * Text anchor. read-only @@ -144,6 +146,7 @@ let line = 0; let lastSpace = -1; let lastSpaceWidth = 0; + let spacesRemoved = 0; let maxLineHeight = 0; for (let i = 0; i < this.text.length; i++) @@ -168,11 +171,12 @@ continue; } - if (lastSpace !== -1 && this.maxWidth > 0 && pos.x * scale > this.maxWidth) + if (lastSpace !== -1 && this._maxWidth > 0 && pos.x * scale > this._maxWidth) { - core.utils.removeItems(chars, lastSpace, i - lastSpace); + core.utils.removeItems(chars, lastSpace - spacesRemoved, i - lastSpace); i = lastSpace; lastSpace = -1; + ++spacesRemoved; lineWidths.push(lastSpaceWidth); maxLineWidth = Math.max(maxLineWidth, lastSpaceWidth); @@ -275,7 +279,7 @@ this._glyphs[i].y -= this._textHeight * this.anchor.y; } } - this.maxLineHeight = maxLineHeight * scale; + this._maxLineHeight = maxLineHeight * scale; } /** @@ -430,6 +434,42 @@ } /** + * The max width of this bitmap text in pixels. If the text provided is longer than the + * value provided, line breaks will be automatically inserted in the last whitespace. + * Disable by setting value to 0 + * + * @member {number} + */ + get maxWidth() + { + return this._maxWidth; + } + + set maxWidth(value) // eslint-disable-line require-jsdoc + { + if (this._maxWidth === value) + { + return; + } + this._maxWidth = value; + this.dirty = true; + } + + /** + * The max line height. This is useful when trying to use the total height of the Text, + * ie: when trying to vertically align. + * + * @member {number} + * @readonly + */ + get maxLineHeight() + { + this.validate(); + + return this._maxLineHeight; + } + + /** * The width of the overall text, different from fontSize, * which is defined in the style object * diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 72fee21..6282341 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -206,7 +206,8 @@ } else { - tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x, -texture._frame.y); + tempCanvas.context.drawImage(baseTexture.source, + -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } @@ -310,11 +311,11 @@ const height = this._height; const x1 = -width * this.anchor._x; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { const y1 = -height * this.anchor._y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 5734fc0..49c3a29 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -4,7 +4,6 @@ import { join } from 'path'; const tempMat = new core.Matrix(); -const tempArray = new Float32Array(4); /** * WebGL renderer plugin for tiling sprites @@ -141,17 +140,13 @@ } shader.uniforms.uTransform = tempMat.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(ts.tint, color); - color[3] = ts.worldAlpha; - shader.uniforms.uColor = color; + shader.uniforms.uColor = core.utils.premultiplyTintToRgba(ts.tint, ts.worldAlpha, + shader.uniforms.uColor, baseTex.premultipliedAlpha); shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); shader.uniforms.uSampler = renderer.bindTexture(tex); - renderer.setBlendMode(ts.blendMode); + renderer.setBlendMode(core.utils.correctBlendMode(ts.blendMode, baseTex.premultipliedAlpha)); quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); } diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 9d59adb..d1f599f 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -104,7 +104,7 @@ glData.shader.uniforms.uSampler = renderer.bindTexture(texture); - renderer.state.setBlendMode(mesh.blendMode); + renderer.state.setBlendMode(core.utils.correctBlendMode(mesh.blendMode, texture.baseTexture.premultipliedAlpha)); if (glData.shader.uniforms.uTransform) { @@ -118,8 +118,9 @@ } } glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); - glData.shader.uniforms.alpha = mesh.worldAlpha; - glData.shader.uniforms.tint = mesh.tintRgb; + + glData.shader.uniforms.uColor = core.utils.premultiplyRgba(mesh.tintRgb, + mesh.worldAlpha, glData.shader.uniforms.uColor, texture.baseTexture.premultipliedAlpha); const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag index 9e0b634..6096983 100644 --- a/src/mesh/webgl/mesh.frag +++ b/src/mesh/webgl/mesh.frag @@ -1,10 +1,9 @@ varying vec2 vTextureCoord; -uniform float alpha; -uniform vec3 tint; +uniform vec4 uColor; uniform sampler2D uSampler; void main(void) { - gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); + gl_FragColor = texture2D(uSampler, vTextureCoord) * uColor; } diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index 6d98098..d6dc59d 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -131,8 +131,8 @@ * @member {number} * @default 0xFFFFFF */ - this._tint = null; - this._tintRGB = []; + this._tint = 0; + this.tintRgb = new Float32Array(4); this.tint = 0xFFFFFF; } @@ -180,7 +180,7 @@ set tint(value) // eslint-disable-line require-jsdoc { this._tint = value; - hex2rgb(value, this._tintRGB); + hex2rgb(value, this.tintRgb); } /** diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js index 16e0c9c..aaf3638 100644 --- a/src/particles/webgl/ParticleRenderer.js +++ b/src/particles/webgl/ParticleRenderer.js @@ -142,8 +142,10 @@ buffers = container._glBuffers[renderer.CONTEXT_UID] = this.generateBuffers(container); } + const baseTexture = children[0]._texture.baseTexture; + // if the uvs have not updated then no point rendering just yet! - this.renderer.setBlendMode(container.blendMode); + this.renderer.setBlendMode(core.utils.correctBlendMode(container.blendMode, baseTexture.premultipliedAlpha)); const gl = renderer.gl; @@ -152,12 +154,11 @@ m.prepend(renderer._activeRenderTarget.projectionMatrix); this.shader.uniforms.projectionMatrix = m.toArray(true); - this.shader.uniforms.uAlpha = container.worldAlpha; - this.shader.uniforms.tint = container._tintRGB; + + this.shader.uniforms.uColor = core.utils.premultiplyRgba(container.tintRgb, + container.worldAlpha, this.shader.uniforms.uColor, baseTexture.premultipliedAlpha); // make sure the texture is bound.. - const baseTexture = children[0]._texture.baseTexture; - this.shader.uniforms.uSampler = renderer.bindTexture(baseTexture); // now lets upload and render the buffers.. diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js index ebd80d9..4361623 100644 --- a/src/particles/webgl/ParticleShader.js +++ b/src/particles/webgl/ParticleShader.js @@ -48,11 +48,10 @@ 'varying float vColor;', 'uniform sampler2D uSampler;', - 'uniform float uAlpha;', - 'uniform vec3 tint;', + 'uniform vec4 uColor;', 'void main(void){', - ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);', + ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uColor;', ' if (color.a == 0.0) discard;', ' gl_FragColor = color;', '}', diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js index e99de40..703175d 100644 --- a/test/core/BaseTexture.js +++ b/test/core/BaseTexture.js @@ -102,11 +102,20 @@ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); }); - it('destroying a destroyed BaseTexture should not throw an error', function () + it('should not throw an error destroying a destroyed BaseTexture', function () { const baseTexture = new PIXI.BaseTexture(); baseTexture.destroy(); baseTexture.destroy(); }); + + it('should set source.crossOrigin to anonymous if explicitly set', function () + { + cleanCache(); + + const baseTexture = PIXI.BaseTexture.fromImage(URL, true); + + expect(baseTexture.source.crossOrigin).to.equal('anonymous'); + }); }); diff --git a/test/core/CanvasRenderer.js b/test/core/CanvasRenderer.js new file mode 100644 index 0000000..7ebf633 --- /dev/null +++ b/test/core/CanvasRenderer.js @@ -0,0 +1,32 @@ +'use strict'; + +describe('PIXI.CanvasRenderer', function () +{ + it('should default context to rootContext', function () + { + const renderer = new PIXI.CanvasRenderer(1, 1); + + try + { + expect(renderer.context).to.equal(renderer.rootContext); + } + finally + { + renderer.destroy(); + } + }); + + it('should allow clear() to work despite no containers added to the renderer', function () + { + const renderer = new PIXI.CanvasRenderer(1, 1); + + try + { + renderer.clear(); + } + finally + { + renderer.destroy(); + } + }); +}); diff --git a/test/core/Rectangle.js b/test/core/Rectangle.js index 40ca3b0..edd5a99 100644 --- a/test/core/Rectangle.js +++ b/test/core/Rectangle.js @@ -135,7 +135,7 @@ it('should fit a rectangle', function () { - const rect1 = new PIXI.Rectangle(0, 0, -10, -10); + const rect1 = new PIXI.Rectangle(0, 0, 10, 10); const rect2 = new PIXI.Rectangle(-10, -10, 5, 5); rect2.fit(rect1); diff --git a/test/core/Sprite.js b/test/core/Sprite.js index b14419d..c9bc4cb 100755 --- a/test/core/Sprite.js +++ b/test/core/Sprite.js @@ -91,11 +91,26 @@ describe('containsPoint', function () { + const texture = new PIXI.RenderTexture.create(20, 30); + const sprite = new PIXI.Sprite(texture); + it('should return true when point inside', function () { const point = new PIXI.Point(10, 10); - const texture = new PIXI.RenderTexture.create(20, 30); - const sprite = new PIXI.Sprite(texture); + + expect(sprite.containsPoint(point)).to.be.true; + }); + + it('should return true when point on left edge', function () + { + const point = new PIXI.Point(0, 15); + + expect(sprite.containsPoint(point)).to.be.true; + }); + + it('should return true when point on top edge', function () + { + const point = new PIXI.Point(10, 0); expect(sprite.containsPoint(point)).to.be.true; }); @@ -103,8 +118,6 @@ it('should return false when point outside', function () { const point = new PIXI.Point(100, 100); - const texture = new PIXI.RenderTexture.create(20, 30); - const sprite = new PIXI.Sprite(texture); expect(sprite.containsPoint(point)).to.be.false; }); diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 1340fcc..0592b54 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -96,16 +96,18 @@ * Disable by setting value to 0 * * @member {number} + * @private */ - this.maxWidth = 0; + this._maxWidth = 0; /** * The max line height. This is useful when trying to use the total height of the Text, * ie: when trying to vertically align. * * @member {number} + * @private */ - this.maxLineHeight = 0; + this._maxLineHeight = 0; /** * Text anchor. read-only @@ -144,6 +146,7 @@ let line = 0; let lastSpace = -1; let lastSpaceWidth = 0; + let spacesRemoved = 0; let maxLineHeight = 0; for (let i = 0; i < this.text.length; i++) @@ -168,11 +171,12 @@ continue; } - if (lastSpace !== -1 && this.maxWidth > 0 && pos.x * scale > this.maxWidth) + if (lastSpace !== -1 && this._maxWidth > 0 && pos.x * scale > this._maxWidth) { - core.utils.removeItems(chars, lastSpace, i - lastSpace); + core.utils.removeItems(chars, lastSpace - spacesRemoved, i - lastSpace); i = lastSpace; lastSpace = -1; + ++spacesRemoved; lineWidths.push(lastSpaceWidth); maxLineWidth = Math.max(maxLineWidth, lastSpaceWidth); @@ -275,7 +279,7 @@ this._glyphs[i].y -= this._textHeight * this.anchor.y; } } - this.maxLineHeight = maxLineHeight * scale; + this._maxLineHeight = maxLineHeight * scale; } /** @@ -430,6 +434,42 @@ } /** + * The max width of this bitmap text in pixels. If the text provided is longer than the + * value provided, line breaks will be automatically inserted in the last whitespace. + * Disable by setting value to 0 + * + * @member {number} + */ + get maxWidth() + { + return this._maxWidth; + } + + set maxWidth(value) // eslint-disable-line require-jsdoc + { + if (this._maxWidth === value) + { + return; + } + this._maxWidth = value; + this.dirty = true; + } + + /** + * The max line height. This is useful when trying to use the total height of the Text, + * ie: when trying to vertically align. + * + * @member {number} + * @readonly + */ + get maxLineHeight() + { + this.validate(); + + return this._maxLineHeight; + } + + /** * The width of the overall text, different from fontSize, * which is defined in the style object * diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 72fee21..6282341 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -206,7 +206,8 @@ } else { - tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x, -texture._frame.y); + tempCanvas.context.drawImage(baseTexture.source, + -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } @@ -310,11 +311,11 @@ const height = this._height; const x1 = -width * this.anchor._x; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { const y1 = -height * this.anchor._y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 5734fc0..49c3a29 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -4,7 +4,6 @@ import { join } from 'path'; const tempMat = new core.Matrix(); -const tempArray = new Float32Array(4); /** * WebGL renderer plugin for tiling sprites @@ -141,17 +140,13 @@ } shader.uniforms.uTransform = tempMat.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(ts.tint, color); - color[3] = ts.worldAlpha; - shader.uniforms.uColor = color; + shader.uniforms.uColor = core.utils.premultiplyTintToRgba(ts.tint, ts.worldAlpha, + shader.uniforms.uColor, baseTex.premultipliedAlpha); shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); shader.uniforms.uSampler = renderer.bindTexture(tex); - renderer.setBlendMode(ts.blendMode); + renderer.setBlendMode(core.utils.correctBlendMode(ts.blendMode, baseTex.premultipliedAlpha)); quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); } diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 9d59adb..d1f599f 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -104,7 +104,7 @@ glData.shader.uniforms.uSampler = renderer.bindTexture(texture); - renderer.state.setBlendMode(mesh.blendMode); + renderer.state.setBlendMode(core.utils.correctBlendMode(mesh.blendMode, texture.baseTexture.premultipliedAlpha)); if (glData.shader.uniforms.uTransform) { @@ -118,8 +118,9 @@ } } glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); - glData.shader.uniforms.alpha = mesh.worldAlpha; - glData.shader.uniforms.tint = mesh.tintRgb; + + glData.shader.uniforms.uColor = core.utils.premultiplyRgba(mesh.tintRgb, + mesh.worldAlpha, glData.shader.uniforms.uColor, texture.baseTexture.premultipliedAlpha); const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag index 9e0b634..6096983 100644 --- a/src/mesh/webgl/mesh.frag +++ b/src/mesh/webgl/mesh.frag @@ -1,10 +1,9 @@ varying vec2 vTextureCoord; -uniform float alpha; -uniform vec3 tint; +uniform vec4 uColor; uniform sampler2D uSampler; void main(void) { - gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); + gl_FragColor = texture2D(uSampler, vTextureCoord) * uColor; } diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index 6d98098..d6dc59d 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -131,8 +131,8 @@ * @member {number} * @default 0xFFFFFF */ - this._tint = null; - this._tintRGB = []; + this._tint = 0; + this.tintRgb = new Float32Array(4); this.tint = 0xFFFFFF; } @@ -180,7 +180,7 @@ set tint(value) // eslint-disable-line require-jsdoc { this._tint = value; - hex2rgb(value, this._tintRGB); + hex2rgb(value, this.tintRgb); } /** diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js index 16e0c9c..aaf3638 100644 --- a/src/particles/webgl/ParticleRenderer.js +++ b/src/particles/webgl/ParticleRenderer.js @@ -142,8 +142,10 @@ buffers = container._glBuffers[renderer.CONTEXT_UID] = this.generateBuffers(container); } + const baseTexture = children[0]._texture.baseTexture; + // if the uvs have not updated then no point rendering just yet! - this.renderer.setBlendMode(container.blendMode); + this.renderer.setBlendMode(core.utils.correctBlendMode(container.blendMode, baseTexture.premultipliedAlpha)); const gl = renderer.gl; @@ -152,12 +154,11 @@ m.prepend(renderer._activeRenderTarget.projectionMatrix); this.shader.uniforms.projectionMatrix = m.toArray(true); - this.shader.uniforms.uAlpha = container.worldAlpha; - this.shader.uniforms.tint = container._tintRGB; + + this.shader.uniforms.uColor = core.utils.premultiplyRgba(container.tintRgb, + container.worldAlpha, this.shader.uniforms.uColor, baseTexture.premultipliedAlpha); // make sure the texture is bound.. - const baseTexture = children[0]._texture.baseTexture; - this.shader.uniforms.uSampler = renderer.bindTexture(baseTexture); // now lets upload and render the buffers.. diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js index ebd80d9..4361623 100644 --- a/src/particles/webgl/ParticleShader.js +++ b/src/particles/webgl/ParticleShader.js @@ -48,11 +48,10 @@ 'varying float vColor;', 'uniform sampler2D uSampler;', - 'uniform float uAlpha;', - 'uniform vec3 tint;', + 'uniform vec4 uColor;', 'void main(void){', - ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);', + ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uColor;', ' if (color.a == 0.0) discard;', ' gl_FragColor = color;', '}', diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js index e99de40..703175d 100644 --- a/test/core/BaseTexture.js +++ b/test/core/BaseTexture.js @@ -102,11 +102,20 @@ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); }); - it('destroying a destroyed BaseTexture should not throw an error', function () + it('should not throw an error destroying a destroyed BaseTexture', function () { const baseTexture = new PIXI.BaseTexture(); baseTexture.destroy(); baseTexture.destroy(); }); + + it('should set source.crossOrigin to anonymous if explicitly set', function () + { + cleanCache(); + + const baseTexture = PIXI.BaseTexture.fromImage(URL, true); + + expect(baseTexture.source.crossOrigin).to.equal('anonymous'); + }); }); diff --git a/test/core/CanvasRenderer.js b/test/core/CanvasRenderer.js new file mode 100644 index 0000000..7ebf633 --- /dev/null +++ b/test/core/CanvasRenderer.js @@ -0,0 +1,32 @@ +'use strict'; + +describe('PIXI.CanvasRenderer', function () +{ + it('should default context to rootContext', function () + { + const renderer = new PIXI.CanvasRenderer(1, 1); + + try + { + expect(renderer.context).to.equal(renderer.rootContext); + } + finally + { + renderer.destroy(); + } + }); + + it('should allow clear() to work despite no containers added to the renderer', function () + { + const renderer = new PIXI.CanvasRenderer(1, 1); + + try + { + renderer.clear(); + } + finally + { + renderer.destroy(); + } + }); +}); diff --git a/test/core/Rectangle.js b/test/core/Rectangle.js index 40ca3b0..edd5a99 100644 --- a/test/core/Rectangle.js +++ b/test/core/Rectangle.js @@ -135,7 +135,7 @@ it('should fit a rectangle', function () { - const rect1 = new PIXI.Rectangle(0, 0, -10, -10); + const rect1 = new PIXI.Rectangle(0, 0, 10, 10); const rect2 = new PIXI.Rectangle(-10, -10, 5, 5); rect2.fit(rect1); diff --git a/test/core/Sprite.js b/test/core/Sprite.js index b14419d..c9bc4cb 100755 --- a/test/core/Sprite.js +++ b/test/core/Sprite.js @@ -91,11 +91,26 @@ describe('containsPoint', function () { + const texture = new PIXI.RenderTexture.create(20, 30); + const sprite = new PIXI.Sprite(texture); + it('should return true when point inside', function () { const point = new PIXI.Point(10, 10); - const texture = new PIXI.RenderTexture.create(20, 30); - const sprite = new PIXI.Sprite(texture); + + expect(sprite.containsPoint(point)).to.be.true; + }); + + it('should return true when point on left edge', function () + { + const point = new PIXI.Point(0, 15); + + expect(sprite.containsPoint(point)).to.be.true; + }); + + it('should return true when point on top edge', function () + { + const point = new PIXI.Point(10, 0); expect(sprite.containsPoint(point)).to.be.true; }); @@ -103,8 +118,6 @@ it('should return false when point outside', function () { const point = new PIXI.Point(100, 100); - const texture = new PIXI.RenderTexture.create(20, 30); - const sprite = new PIXI.Sprite(texture); expect(sprite.containsPoint(point)).to.be.false; }); diff --git a/test/core/TilingSprite.js b/test/core/TilingSprite.js index 7694792..0b6c87f 100644 --- a/test/core/TilingSprite.js +++ b/test/core/TilingSprite.js @@ -30,7 +30,9 @@ const texture = new PIXI.Texture(new PIXI.BaseTexture()); const tilingSprite = new PIXI.extras.TilingSprite(texture, 200, 300); - expect(tilingSprite.containsPoint(new PIXI.Point(1, 1))).to.equal(true); + expect(tilingSprite.containsPoint(new PIXI.Point(0, 0))).to.equal(true); + expect(tilingSprite.containsPoint(new PIXI.Point(10, 10))).to.equal(true); + expect(tilingSprite.containsPoint(new PIXI.Point(200, 300))).to.equal(false); expect(tilingSprite.containsPoint(new PIXI.Point(300, 400))).to.equal(false); }); diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 1340fcc..0592b54 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -96,16 +96,18 @@ * Disable by setting value to 0 * * @member {number} + * @private */ - this.maxWidth = 0; + this._maxWidth = 0; /** * The max line height. This is useful when trying to use the total height of the Text, * ie: when trying to vertically align. * * @member {number} + * @private */ - this.maxLineHeight = 0; + this._maxLineHeight = 0; /** * Text anchor. read-only @@ -144,6 +146,7 @@ let line = 0; let lastSpace = -1; let lastSpaceWidth = 0; + let spacesRemoved = 0; let maxLineHeight = 0; for (let i = 0; i < this.text.length; i++) @@ -168,11 +171,12 @@ continue; } - if (lastSpace !== -1 && this.maxWidth > 0 && pos.x * scale > this.maxWidth) + if (lastSpace !== -1 && this._maxWidth > 0 && pos.x * scale > this._maxWidth) { - core.utils.removeItems(chars, lastSpace, i - lastSpace); + core.utils.removeItems(chars, lastSpace - spacesRemoved, i - lastSpace); i = lastSpace; lastSpace = -1; + ++spacesRemoved; lineWidths.push(lastSpaceWidth); maxLineWidth = Math.max(maxLineWidth, lastSpaceWidth); @@ -275,7 +279,7 @@ this._glyphs[i].y -= this._textHeight * this.anchor.y; } } - this.maxLineHeight = maxLineHeight * scale; + this._maxLineHeight = maxLineHeight * scale; } /** @@ -430,6 +434,42 @@ } /** + * The max width of this bitmap text in pixels. If the text provided is longer than the + * value provided, line breaks will be automatically inserted in the last whitespace. + * Disable by setting value to 0 + * + * @member {number} + */ + get maxWidth() + { + return this._maxWidth; + } + + set maxWidth(value) // eslint-disable-line require-jsdoc + { + if (this._maxWidth === value) + { + return; + } + this._maxWidth = value; + this.dirty = true; + } + + /** + * The max line height. This is useful when trying to use the total height of the Text, + * ie: when trying to vertically align. + * + * @member {number} + * @readonly + */ + get maxLineHeight() + { + this.validate(); + + return this._maxLineHeight; + } + + /** * The width of the overall text, different from fontSize, * which is defined in the style object * diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 72fee21..6282341 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -206,7 +206,8 @@ } else { - tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x, -texture._frame.y); + tempCanvas.context.drawImage(baseTexture.source, + -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } @@ -310,11 +311,11 @@ const height = this._height; const x1 = -width * this.anchor._x; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { const y1 = -height * this.anchor._y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 5734fc0..49c3a29 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -4,7 +4,6 @@ import { join } from 'path'; const tempMat = new core.Matrix(); -const tempArray = new Float32Array(4); /** * WebGL renderer plugin for tiling sprites @@ -141,17 +140,13 @@ } shader.uniforms.uTransform = tempMat.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(ts.tint, color); - color[3] = ts.worldAlpha; - shader.uniforms.uColor = color; + shader.uniforms.uColor = core.utils.premultiplyTintToRgba(ts.tint, ts.worldAlpha, + shader.uniforms.uColor, baseTex.premultipliedAlpha); shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); shader.uniforms.uSampler = renderer.bindTexture(tex); - renderer.setBlendMode(ts.blendMode); + renderer.setBlendMode(core.utils.correctBlendMode(ts.blendMode, baseTex.premultipliedAlpha)); quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); } diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 9d59adb..d1f599f 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -104,7 +104,7 @@ glData.shader.uniforms.uSampler = renderer.bindTexture(texture); - renderer.state.setBlendMode(mesh.blendMode); + renderer.state.setBlendMode(core.utils.correctBlendMode(mesh.blendMode, texture.baseTexture.premultipliedAlpha)); if (glData.shader.uniforms.uTransform) { @@ -118,8 +118,9 @@ } } glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); - glData.shader.uniforms.alpha = mesh.worldAlpha; - glData.shader.uniforms.tint = mesh.tintRgb; + + glData.shader.uniforms.uColor = core.utils.premultiplyRgba(mesh.tintRgb, + mesh.worldAlpha, glData.shader.uniforms.uColor, texture.baseTexture.premultipliedAlpha); const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag index 9e0b634..6096983 100644 --- a/src/mesh/webgl/mesh.frag +++ b/src/mesh/webgl/mesh.frag @@ -1,10 +1,9 @@ varying vec2 vTextureCoord; -uniform float alpha; -uniform vec3 tint; +uniform vec4 uColor; uniform sampler2D uSampler; void main(void) { - gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); + gl_FragColor = texture2D(uSampler, vTextureCoord) * uColor; } diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index 6d98098..d6dc59d 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -131,8 +131,8 @@ * @member {number} * @default 0xFFFFFF */ - this._tint = null; - this._tintRGB = []; + this._tint = 0; + this.tintRgb = new Float32Array(4); this.tint = 0xFFFFFF; } @@ -180,7 +180,7 @@ set tint(value) // eslint-disable-line require-jsdoc { this._tint = value; - hex2rgb(value, this._tintRGB); + hex2rgb(value, this.tintRgb); } /** diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js index 16e0c9c..aaf3638 100644 --- a/src/particles/webgl/ParticleRenderer.js +++ b/src/particles/webgl/ParticleRenderer.js @@ -142,8 +142,10 @@ buffers = container._glBuffers[renderer.CONTEXT_UID] = this.generateBuffers(container); } + const baseTexture = children[0]._texture.baseTexture; + // if the uvs have not updated then no point rendering just yet! - this.renderer.setBlendMode(container.blendMode); + this.renderer.setBlendMode(core.utils.correctBlendMode(container.blendMode, baseTexture.premultipliedAlpha)); const gl = renderer.gl; @@ -152,12 +154,11 @@ m.prepend(renderer._activeRenderTarget.projectionMatrix); this.shader.uniforms.projectionMatrix = m.toArray(true); - this.shader.uniforms.uAlpha = container.worldAlpha; - this.shader.uniforms.tint = container._tintRGB; + + this.shader.uniforms.uColor = core.utils.premultiplyRgba(container.tintRgb, + container.worldAlpha, this.shader.uniforms.uColor, baseTexture.premultipliedAlpha); // make sure the texture is bound.. - const baseTexture = children[0]._texture.baseTexture; - this.shader.uniforms.uSampler = renderer.bindTexture(baseTexture); // now lets upload and render the buffers.. diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js index ebd80d9..4361623 100644 --- a/src/particles/webgl/ParticleShader.js +++ b/src/particles/webgl/ParticleShader.js @@ -48,11 +48,10 @@ 'varying float vColor;', 'uniform sampler2D uSampler;', - 'uniform float uAlpha;', - 'uniform vec3 tint;', + 'uniform vec4 uColor;', 'void main(void){', - ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);', + ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uColor;', ' if (color.a == 0.0) discard;', ' gl_FragColor = color;', '}', diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js index e99de40..703175d 100644 --- a/test/core/BaseTexture.js +++ b/test/core/BaseTexture.js @@ -102,11 +102,20 @@ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); }); - it('destroying a destroyed BaseTexture should not throw an error', function () + it('should not throw an error destroying a destroyed BaseTexture', function () { const baseTexture = new PIXI.BaseTexture(); baseTexture.destroy(); baseTexture.destroy(); }); + + it('should set source.crossOrigin to anonymous if explicitly set', function () + { + cleanCache(); + + const baseTexture = PIXI.BaseTexture.fromImage(URL, true); + + expect(baseTexture.source.crossOrigin).to.equal('anonymous'); + }); }); diff --git a/test/core/CanvasRenderer.js b/test/core/CanvasRenderer.js new file mode 100644 index 0000000..7ebf633 --- /dev/null +++ b/test/core/CanvasRenderer.js @@ -0,0 +1,32 @@ +'use strict'; + +describe('PIXI.CanvasRenderer', function () +{ + it('should default context to rootContext', function () + { + const renderer = new PIXI.CanvasRenderer(1, 1); + + try + { + expect(renderer.context).to.equal(renderer.rootContext); + } + finally + { + renderer.destroy(); + } + }); + + it('should allow clear() to work despite no containers added to the renderer', function () + { + const renderer = new PIXI.CanvasRenderer(1, 1); + + try + { + renderer.clear(); + } + finally + { + renderer.destroy(); + } + }); +}); diff --git a/test/core/Rectangle.js b/test/core/Rectangle.js index 40ca3b0..edd5a99 100644 --- a/test/core/Rectangle.js +++ b/test/core/Rectangle.js @@ -135,7 +135,7 @@ it('should fit a rectangle', function () { - const rect1 = new PIXI.Rectangle(0, 0, -10, -10); + const rect1 = new PIXI.Rectangle(0, 0, 10, 10); const rect2 = new PIXI.Rectangle(-10, -10, 5, 5); rect2.fit(rect1); diff --git a/test/core/Sprite.js b/test/core/Sprite.js index b14419d..c9bc4cb 100755 --- a/test/core/Sprite.js +++ b/test/core/Sprite.js @@ -91,11 +91,26 @@ describe('containsPoint', function () { + const texture = new PIXI.RenderTexture.create(20, 30); + const sprite = new PIXI.Sprite(texture); + it('should return true when point inside', function () { const point = new PIXI.Point(10, 10); - const texture = new PIXI.RenderTexture.create(20, 30); - const sprite = new PIXI.Sprite(texture); + + expect(sprite.containsPoint(point)).to.be.true; + }); + + it('should return true when point on left edge', function () + { + const point = new PIXI.Point(0, 15); + + expect(sprite.containsPoint(point)).to.be.true; + }); + + it('should return true when point on top edge', function () + { + const point = new PIXI.Point(10, 0); expect(sprite.containsPoint(point)).to.be.true; }); @@ -103,8 +118,6 @@ it('should return false when point outside', function () { const point = new PIXI.Point(100, 100); - const texture = new PIXI.RenderTexture.create(20, 30); - const sprite = new PIXI.Sprite(texture); expect(sprite.containsPoint(point)).to.be.false; }); diff --git a/test/core/TilingSprite.js b/test/core/TilingSprite.js index 7694792..0b6c87f 100644 --- a/test/core/TilingSprite.js +++ b/test/core/TilingSprite.js @@ -30,7 +30,9 @@ const texture = new PIXI.Texture(new PIXI.BaseTexture()); const tilingSprite = new PIXI.extras.TilingSprite(texture, 200, 300); - expect(tilingSprite.containsPoint(new PIXI.Point(1, 1))).to.equal(true); + expect(tilingSprite.containsPoint(new PIXI.Point(0, 0))).to.equal(true); + expect(tilingSprite.containsPoint(new PIXI.Point(10, 10))).to.equal(true); + expect(tilingSprite.containsPoint(new PIXI.Point(200, 300))).to.equal(false); expect(tilingSprite.containsPoint(new PIXI.Point(300, 400))).to.equal(false); }); diff --git a/test/core/WebGLRenderer.js b/test/core/WebGLRenderer.js index 3d6cf98..4fe37a8 100644 --- a/test/core/WebGLRenderer.js +++ b/test/core/WebGLRenderer.js @@ -18,4 +18,18 @@ renderer.destroy(); } })); + + it('should allow clear() to work despite no containers added to the renderer', withGL(function () + { + const renderer = new PIXI.WebGLRenderer(1, 1); + + try + { + renderer.clear(); + } + finally + { + renderer.destroy(); + } + })); }); diff --git a/LICENSE b/LICENSE index 7ca4d7c..d2dcf81 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -The Pixi License +The MIT License Copyright (c) 2013-2016 Mathew Groves, Chad Engler diff --git a/src/core/Application.js b/src/core/Application.js index 2fcc729..2c2c274 100644 --- a/src/core/Application.js +++ b/src/core/Application.js @@ -33,11 +33,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present - * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility - * with older / less advanced devices. If you experience unexplained flickering try setting this to true. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @param {boolean} [options.sharedTicker=false] - `true` to use PIXI.ticker.shared, `false` to create new ticker. * @param {boolean} [options.sharedLoader=false] - `true` to use PIXI.loaders.shared, `false` to create new Loader. */ diff --git a/src/core/autoDetectRenderer.js b/src/core/autoDetectRenderer.js index 9ec0364..51bb5b6 100644 --- a/src/core/autoDetectRenderer.js +++ b/src/core/autoDetectRenderer.js @@ -17,9 +17,19 @@ * @param {boolean} [options.transparent=false] - If the render view is transparent, default false * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, enable this if you - * need to call toDataUrl on the webgl context + * need to call toDataUrl on the webgl context + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or + * not before the new render pass. * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer, retina would be 2 * @param {boolean} [options.forceCanvas=false] - prevents selection of WebGL renderer, even if such is present + * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when rendering, + * stopping pixel interpolation. + * @param {boolean} [options.forceFXAA=false] - forces FXAA antialiasing to be used over native. + * FXAA is faster, but may not always look as great **webgl only** + * @param {boolean} [options.legacy=false] - `true` to ensure compatibility with older / less advanced devices. + * If you experience unexplained flickering try setting this to true. **webgl only** * @return {PIXI.WebGLRenderer|PIXI.CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer */ export function autoDetectRenderer(options, arg1, arg2, arg3) diff --git a/src/core/const.js b/src/core/const.js index a26d950..b3ee851 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -104,6 +104,9 @@ SATURATION: 14, COLOR: 15, LUMINOSITY: 16, + NORMAL_NPM: 17, + ADD_NPM: 18, + SCREEN_NPM: 19, }; /** diff --git a/src/core/renderers/SystemRenderer.js b/src/core/renderers/SystemRenderer.js index b9f192d..01e0312 100644 --- a/src/core/renderers/SystemRenderer.js +++ b/src/core/renderers/SystemRenderer.js @@ -31,7 +31,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -295,7 +297,6 @@ this._backgroundColorRgba = null; this._backgroundColorString = null; - this.backgroundColor = 0; this._tempDisplayObjectParent = null; this._lastObjectRendered = null; } diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 691caf0..1907b6c 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -28,7 +28,9 @@ * @param {boolean} [options.antialias=false] - sets antialias (only applicable in chrome at the moment) * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. The * resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear the canvas or + * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, + * enable this if you need to call toDataUrl on the webgl context. + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear the canvas or * not before the new render pass. * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area * (shown if not transparent). @@ -42,13 +44,20 @@ this.type = RENDERER_TYPE.CANVAS; /** - * The canvas 2d context that everything is drawn with. + * The root canvas 2d context that everything is drawn with. * * @member {CanvasRenderingContext2D} */ this.rootContext = this.view.getContext('2d', { alpha: this.transparent }); /** + * The currently active canvas 2d context (could change with renderTextures) + * + * @member {CanvasRenderingContext2D} + */ + this.context = this.rootContext; + + /** * Boolean flag controlling canvas refresh. * * @member {boolean} @@ -94,7 +103,6 @@ this.blendModes = mapCanvasBlendModesToPixi(); this._activeBlendMode = null; - this.context = null; this.renderingToScreen = false; this.resize(this.options.width, this.options.height); diff --git a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js index 7bf613b..6a4759d 100644 --- a/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js +++ b/src/core/renderers/canvas/utils/mapCanvasBlendModesToPixi.js @@ -53,6 +53,10 @@ array[BLEND_MODES.COLOR] = 'source-over'; array[BLEND_MODES.LUMINOSITY] = 'source-over'; } + // not-premultiplied, only for webgl + array[BLEND_MODES.NORMAL_NPM] = array[BLEND_MODES.NORMAL]; + array[BLEND_MODES.ADD_NPM] = array[BLEND_MODES.ADD]; + array[BLEND_MODES.SCREEN_NPM] = array[BLEND_MODES.SCREEN]; return array; } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index dd53b06..096ceb1 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -43,13 +43,15 @@ * FXAA is faster, but may not always look as great * @param {number} [options.resolution=1] - The resolution / device pixel ratio of the renderer. * The resolution of the renderer retina would be 2. - * @param {boolean} [options.clearBeforeRender=true] - This sets if the CanvasRenderer will clear + * @param {boolean} [options.clearBeforeRender=true] - This sets if the renderer will clear * the canvas or not before the new render pass. If you wish to set this to false, you *must* set * preserveDrawingBuffer to `true`. * @param {boolean} [options.preserveDrawingBuffer=false] - enables drawing buffer preservation, * enable this if you need to call toDataUrl on the webgl context. * @param {boolean} [options.roundPixels=false] - If true Pixi will Math.floor() x/y values when * rendering, stopping pixel interpolation. + * @param {number} [options.backgroundColor=0x000000] - The background color of the rendered area + * (shown if not transparent). * @param {boolean} [options.legacy=false] - If true Pixi will aim to ensure compatibility * with older / less advanced devices. If you experiance unexplained flickering try setting this to true. */ @@ -525,7 +527,7 @@ * @param {PIXI.Texture} texture - the new texture * @param {number} location - the suggested texture location * @param {boolean} forceLocation - force the location - * @return {PIXI.WebGLRenderer} Returns itself. + * @return {number} bound texture location */ bindTexture(texture, location, forceLocation) { diff --git a/src/core/renderers/webgl/WebGLState.js b/src/core/renderers/webgl/WebGLState.js index 6c846d5..cbc0b7a 100755 --- a/src/core/renderers/webgl/WebGLState.js +++ b/src/core/renderers/webgl/WebGLState.js @@ -155,7 +155,16 @@ this.activeState[BLEND_FUNC] = value; - this.gl.blendFunc(this.blendModes[value][0], this.blendModes[value][1]); + const mode = this.blendModes[value]; + + if (mode.length === 2) + { + this.gl.blendFunc(mode[0], mode[1]); + } + else + { + this.gl.blendFuncSeparate(mode[0], mode[1], mode[2], mode[3]); + } } /** diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 6d2467f..4cf5b67 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -203,7 +203,7 @@ setFrame(destinationFrame, sourceFrame) { this.destinationFrame = destinationFrame || this.destinationFrame || this.defaultFrame; - this.sourceFrame = sourceFrame || this.sourceFrame || destinationFrame; + this.sourceFrame = sourceFrame || this.sourceFrame || this.destinationFrame; } /** diff --git a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js index 53e2111..5f4ef92 100644 --- a/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js +++ b/src/core/renderers/webgl/utils/mapWebGLBlendModesToPixi.js @@ -32,5 +32,10 @@ array[BLEND_MODES.COLOR] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; array[BLEND_MODES.LUMINOSITY] = [gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + // not-premultiplied blend modes + array[BLEND_MODES.NORMAL_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA]; + array[BLEND_MODES.ADD_NPM] = [gl.SRC_ALPHA, gl.DST_ALPHA, gl.ONE, gl.DST_ALPHA]; + array[BLEND_MODES.SCREEN_NPM] = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_COLOR, gl.ONE, gl.ONE_MINUS_SRC_COLOR]; + return array; } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index cd574ac..4f9ae43 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -390,11 +390,11 @@ const x1 = -width * this.anchor.x; let y1 = 0; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { y1 = -height * this.anchor.y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 9ddc126..8fd44c1 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -5,6 +5,7 @@ import checkMaxIfStatmentsInShader from '../../renderers/webgl/utils/checkMaxIfStatmentsInShader'; import Buffer from './BatchBuffer'; import settings from '../../settings'; +import { premultiplyBlendMode, premultiplyTint } from '../../utils'; import glCore from 'pixi-gl-core'; import bitTwiddle from 'bit-twiddle'; @@ -226,7 +227,8 @@ let currentGroup = groups[0]; let vertexData; let uvs; - let blendMode = sprites[0].blendMode; + let blendMode = premultiplyBlendMode[ + sprites[0]._texture.baseTexture.premultipliedAlpha ? 1 : 0][sprites[0].blendMode]; currentGroup.textureCount = 0; currentGroup.start = 0; @@ -251,10 +253,12 @@ nextTexture = sprite._texture.baseTexture; - if (blendMode !== sprite.blendMode) + const spriteBlendMode = premultiplyBlendMode[Number(nextTexture.premultipliedAlpha)][sprite.blendMode]; + + if (blendMode !== spriteBlendMode) { // finish a group.. - blendMode = sprite.blendMode; + blendMode = spriteBlendMode; // force the batch to break! currentTexture = null; @@ -362,10 +366,13 @@ uint32View[index + 7] = uvs[1]; uint32View[index + 12] = uvs[2]; uint32View[index + 17] = uvs[3]; - /* eslint-disable max-len */ - uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = sprite._tintRGB + (Math.min(sprite.worldAlpha, 1) * 255 << 24); + const alpha = Math.min(sprite.worldAlpha, 1.0); + // we dont call extra function if alpha is 1.0, that's faster + const argb = alpha < 1.0 && nextTexture.premultipliedAlpha ? premultiplyTint(sprite._tintRGB, alpha) + : sprite._tintRGB + (alpha * 255 << 24); + uint32View[index + 3] = uint32View[index + 8] = uint32View[index + 13] = uint32View[index + 18] = argb; float32View[index + 4] = float32View[index + 9] = float32View[index + 14] = float32View[index + 19] = nextTexture._virtalBoundId; /* eslint-enable max-len */ diff --git a/src/core/sprites/webgl/texture.vert b/src/core/sprites/webgl/texture.vert index 81817b1..18b89ff 100644 --- a/src/core/sprites/webgl/texture.vert +++ b/src/core/sprites/webgl/texture.vert @@ -15,5 +15,5 @@ vTextureCoord = aTextureCoord; vTextureId = aTextureId; - vColor = vec4(aColor.rgb * aColor.a, aColor.a); + vColor = aColor; } diff --git a/src/core/text/Text.js b/src/core/text/Text.js index d81216d..29dd720 100644 --- a/src/core/text/Text.js +++ b/src/core/text/Text.js @@ -163,17 +163,14 @@ if (style.dropShadow) { - context.shadowBlur = style.dropShadowBlur; + context.fillStyle = style.dropShadowColor; context.globalAlpha = style.dropShadowAlpha; + context.shadowBlur = style.dropShadowBlur; if (style.dropShadowBlur > 0) { context.shadowColor = style.dropShadowColor; } - else - { - context.fillStyle = style.dropShadowColor; - } const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance; const yShadowOffset = Math.sin(style.dropShadowAngle) * style.dropShadowDistance; diff --git a/src/core/text/TextStyle.js b/src/core/text/TextStyle.js index 4d66953..71f31e2 100644 --- a/src/core/text/TextStyle.js +++ b/src/core/text/TextStyle.js @@ -11,7 +11,7 @@ dropShadowAlpha: 1, dropShadowAngle: Math.PI / 6, dropShadowBlur: 0, - dropShadowColor: '#000000', + dropShadowColor: 'black', dropShadowDistance: 5, fill: 'black', fillGradientType: TEXT_GRADIENT.LINEAR_VERTICAL, @@ -53,7 +53,7 @@ * @param {number} [style.dropShadowAlpha=1] - Set alpha for the drop shadow * @param {number} [style.dropShadowAngle=Math.PI/6] - Set a angle of the drop shadow * @param {number} [style.dropShadowBlur=0] - Set a shadow blur radius - * @param {string} [style.dropShadowColor='#000000'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' + * @param {string} [style.dropShadowColor='black'] - A fill style to be used on the dropshadow e.g 'red', '#00FF00' * @param {number} [style.dropShadowDistance=5] - Set a distance of the drop shadow * @param {string|string[]|number|number[]|CanvasGradient|CanvasPattern} [style.fill='black'] - A canvas * fillstyle that will be used on the text e.g 'red', '#00FF00'. Can be an array to create a gradient diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 93c3406..0d223a8 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -683,6 +683,10 @@ { image.crossOrigin = determineCrossOrigin(imageUrl); } + else if (crossorigin) + { + image.crossOrigin = typeof crossorigin === 'string' ? crossorigin : 'anonymous'; + } baseTexture = new BaseTexture(image, scaleMode); baseTexture.imageUrl = imageUrl; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 22e2d98..5f0da0f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ import * as mixins from './mixin'; import * as isMobile from 'ismobilejs'; import removeItems from 'remove-array-items'; +import mapPremultipliedBlendModes from './mapPremultipliedBlendModes'; let nextUid = 0; let saidHello = false; @@ -397,3 +398,107 @@ delete BaseTextureCache[key]; } } + +/** + * @memberof PIXI.utils + * @const premultiplyBlendMode + * @type {Array} maps premultiply flag and blendMode to adjusted blendMode + */ +export const premultiplyBlendMode = mapPremultipliedBlendModes(); + +/** + * changes blendMode according to texture format + * + * @memberof PIXI.utils + * @function correctBlendMode + * @param {number} blendMode supposed blend mode + * @param {boolean} premultiplied whether source is premultiplied + * @returns {number} true blend mode for this texture + */ +export function correctBlendMode(blendMode, premultiplied) +{ + return premultiplyBlendMode[premultiplied ? 1 : 0][blendMode]; +} + +/** + * premultiplies tint + * + * @param {number} tint integet RGB + * @param {number} alpha floating point alpha (0.0-1.0) + * @returns {number} tint multiplied by alpha + */ +export function premultiplyTint(tint, alpha) +{ + if (alpha === 1.0) + { + return (alpha * 255 << 24) + tint; + } + if (alpha === 0.0) + { + return 0; + } + let R = ((tint >> 16) & 0xFF); + let G = ((tint >> 8) & 0xFF); + let B = (tint & 0xFF); + + R = ((R * alpha) + 0.5) | 0; + G = ((G * alpha) + 0.5) | 0; + B = ((B * alpha) + 0.5) | 0; + + return (alpha * 255 << 24) + (R << 16) + (G << 8) + B; +} + +/** + * combines rgb and alpha to out array + * + * @param {Float32Array|number[]} rgb input rgb + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyRgba(rgb, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + if (premultiply || premultiply === undefined) + { + out[0] = rgb[0] * alpha; + out[1] = rgb[1] * alpha; + out[2] = rgb[2] * alpha; + } + else + { + out[0] = rgb[0]; + out[1] = rgb[1]; + out[2] = rgb[2]; + } + out[3] = alpha; + + return out; +} + +/** + * converts integer tint and float alpha to vec4 form, premultiplies by default + * + * @param {number} tint input tint + * @param {number} alpha alpha param + * @param {Float32Array} [out] output + * @param {boolean} [premultiply=true] do premultiply it + * @returns {Float32Array} vec4 rgba + */ +export function premultiplyTintToRgba(tint, alpha, out, premultiply) +{ + out = out || new Float32Array(4); + out[0] = ((tint >> 16) & 0xFF) / 255.0; + out[1] = ((tint >> 8) & 0xFF) / 255.0; + out[2] = (tint & 0xFF) / 255.0; + if (premultiply || premultiply === undefined) + { + out[0] *= alpha; + out[1] *= alpha; + out[2] *= alpha; + } + out[3] = alpha; + + return out; +} diff --git a/src/core/utils/mapPremultipliedBlendModes.js b/src/core/utils/mapPremultipliedBlendModes.js new file mode 100644 index 0000000..5122986 --- /dev/null +++ b/src/core/utils/mapPremultipliedBlendModes.js @@ -0,0 +1,38 @@ +import { BLEND_MODES } from '../const'; + +/** + * Corrects pixi blend, takes premultiplied alpha into account + * + * @memberof PIXI + * @function mapPremultipliedBlendModes + * @private + * @param {Array} [array] - The array to output into. + * @return {Array} Mapped modes. + */ + +export default function mapPremultipliedBlendModes() +{ + const pm = []; + const npm = []; + + for (let i = 0; i < 32; i++) + { + pm[i] = i; + npm[i] = i; + } + + pm[BLEND_MODES.NORMAL_NPM] = BLEND_MODES.NORMAL; + pm[BLEND_MODES.ADD_NPM] = BLEND_MODES.ADD; + pm[BLEND_MODES.SCREEN_NPM] = BLEND_MODES.SCREEN; + + npm[BLEND_MODES.NORMAL] = BLEND_MODES.NORMAL_NPM; + npm[BLEND_MODES.ADD] = BLEND_MODES.ADD_NPM; + npm[BLEND_MODES.SCREEN] = BLEND_MODES.SCREEN_NPM; + + const array = []; + + array.push(npm); + array.push(pm); + + return array; +} diff --git a/src/deprecation.js b/src/deprecation.js index 063e4df..0f41bb7 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -564,24 +564,27 @@ }, }); - Object.defineProperties(extras, { + if (extras.AnimatedSprite) + { + Object.defineProperties(extras, { - /** - * @class - * @name MovieClip - * @memberof PIXI.extras - * @see PIXI.extras.AnimatedSprite - * @deprecated since version 4.2.0 - */ - MovieClip: { - get() - { - warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); + /** + * @class + * @name MovieClip + * @memberof PIXI.extras + * @see PIXI.extras.AnimatedSprite + * @deprecated since version 4.2.0 + */ + MovieClip: { + get() + { + warn('The MovieClip class has been renamed to AnimatedSprite, please use AnimatedSprite from now on.'); - return extras.AnimatedSprite; + return extras.AnimatedSprite; + }, }, - }, - }); + }); + } core.DisplayObject.prototype.generateTexture = function generateTexture(renderer, scaleMode, resolution) { @@ -646,18 +649,21 @@ warn('setTexture is now deprecated, please use the texture property, e.g : sprite.texture = texture;'); }; - /** - * @method - * @name PIXI.extras.BitmapText#setText - * @see PIXI.extras.BitmapText#text - * @deprecated since version 3.0.0 - * @param {string} text - The text to set to. - */ - extras.BitmapText.prototype.setText = function setText(text) + if (extras.BitmapText) { - this.text = text; - warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); - }; + /** + * @method + * @name PIXI.extras.BitmapText#setText + * @see PIXI.extras.BitmapText#text + * @deprecated since version 3.0.0 + * @param {string} text - The text to set to. + */ + extras.BitmapText.prototype.setText = function setText(text) + { + this.text = text; + warn(`setText is now deprecated, please use the text property, e.g : myBitmapText.text = 'my text';`); + }; + } /** * @method @@ -994,86 +1000,95 @@ }, }); - /** - * @method - * @name PIXI.prepare.BasePrepare#register - * @see PIXI.prepare.BasePrepare#registerFindHook - * @deprecated since version 4.4.2 - * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` - * function must return `true` if it was able to add item to the queue. - * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and - * function must return `true` if it was able to handle upload of item. - * @return {PIXI.BasePrepare} Instance of plugin for chaining. - */ - prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) + if (prepare.BasePrepare) { - warn('renderer.plugins.prepare.register is now deprecated, ' - + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - - if (addHook) + /** + * @method + * @name PIXI.prepare.BasePrepare#register + * @see PIXI.prepare.BasePrepare#registerFindHook + * @deprecated since version 4.4.2 + * @param {Function} [addHook] - Function call that takes two parameters: `item:*, queue:Array` + * function must return `true` if it was able to add item to the queue. + * @param {Function} [uploadHook] - Function call that takes two parameters: `prepare:CanvasPrepare, item:*` and + * function must return `true` if it was able to handle upload of item. + * @return {PIXI.BasePrepare} Instance of plugin for chaining. + */ + prepare.BasePrepare.prototype.register = function register(addHook, uploadHook) { - this.registerFindHook(addHook); - } + warn('renderer.plugins.prepare.register is now deprecated, ' + + 'please use renderer.plugins.prepare.registerFindHook & renderer.plugins.prepare.registerUploadHook'); - if (uploadHook) - { - this.registerUploadHook(uploadHook); - } + if (addHook) + { + this.registerFindHook(addHook); + } - return this; - }; + if (uploadHook) + { + this.registerUploadHook(uploadHook); + } - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return this; + }; + } - return NaN; - }, - }); + if (prepare.canvas) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.canvas.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.canvas, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.CanvasPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); - /** - * The number of graphics or textures to upload to the GPU. - * - * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME - * @static - * @type {number} - * @see PIXI.prepare.BasePrepare.limiter - * @deprecated since 4.2.0 - */ - Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { - set() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' - + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); - // because we don't have a reference to the renderer, we can't actually set - // the uploads per frame, so we'll have to stick with the warning. - }, - get() - { - warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' - + 'renderer.plugins.prepare.limiter'); + return NaN; + }, + }); + } - return NaN; - }, - }); + if (prepare.webgl) + { + /** + * The number of graphics or textures to upload to the GPU. + * + * @name PIXI.prepare.webgl.UPLOADS_PER_FRAME + * @static + * @type {number} + * @see PIXI.prepare.BasePrepare.limiter + * @deprecated since 4.2.0 + */ + Object.defineProperty(prepare.webgl, 'UPLOADS_PER_FRAME', { + set() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please set ' + + 'renderer.plugins.prepare.limiter.maxItemsPerFrame on your renderer'); + // because we don't have a reference to the renderer, we can't actually set + // the uploads per frame, so we'll have to stick with the warning. + }, + get() + { + warn('PIXI.WebGLPrepare.UPLOADS_PER_FRAME has been removed. Please use ' + + 'renderer.plugins.prepare.limiter'); + + return NaN; + }, + }); + } if (loaders.Loader) { @@ -1143,68 +1158,74 @@ }); } - /** - * @name PIXI.interaction.interactiveTarget#defaultCursor - * @static - * @type {number} - * @see PIXI.interaction.interactiveTarget#cursor - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { - set(value) - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); - this.cursor = value; - }, - get() - { - warn('Property defaultCursor has been replaced with \'cursor\'. '); + if (interaction.interactiveTarget) + { + /** + * @name PIXI.interaction.interactiveTarget#defaultCursor + * @static + * @type {number} + * @see PIXI.interaction.interactiveTarget#cursor + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.interactiveTarget, 'defaultCursor', { + set(value) + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); + this.cursor = value; + }, + get() + { + warn('Property defaultCursor has been replaced with \'cursor\'. '); - return this.cursor; - }, - }); + return this.cursor; + }, + }); + } - /** - * @name PIXI.interaction.InteractionManager#defaultCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { - set(value) - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - this.cursorStyles.default = value; - }, - get() - { - warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + if (interaction.InteractionManager) + { + /** + * @name PIXI.interaction.InteractionManager#defaultCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'defaultCursorStyle', { + set(value) + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); + this.cursorStyles.default = value; + }, + get() + { + warn('Property defaultCursorStyle has been replaced with \'cursorStyles.default\'. '); - return this.cursorStyles.default; - }, - }); + return this.cursorStyles.default; + }, + }); - /** - * @name PIXI.interaction.InteractionManager#currentCursorStyle - * @static - * @type {string} - * @see PIXI.interaction.InteractionManager#cursorStyles - * @deprecated since 4.3.0 - */ - Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { - set(value) - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); - this.currentCursorMode = value; - }, - get() - { - warn('Property currentCursorStyle has been removed.' - + 'See the currentCursorMode property, which works differently.'); + /** + * @name PIXI.interaction.InteractionManager#currentCursorStyle + * @static + * @type {string} + * @see PIXI.interaction.InteractionManager#cursorStyles + * @deprecated since 4.3.0 + */ + Object.defineProperty(interaction.InteractionManager, 'currentCursorStyle', { + set(value) + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); + this.currentCursorMode = value; + }, + get() + { + warn('Property currentCursorStyle has been removed.' + + 'See the currentCursorMode property, which works differently.'); - return this.currentCursorMode; - }, - }); + return this.currentCursorMode; + }, + }); + } } diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index a24f07c..992a4e0 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -88,6 +88,13 @@ */ this.onFrameChange = null; + /** + * Function to call when 'loop' is true, and an AnimatedSprite is played and loops around to start again + * + * @member {Function} + */ + this.onLoop = null; + /** * Elapsed time since animation has been started, used internally to display current texture * @@ -239,6 +246,18 @@ } else if (previousFrame !== this.currentFrame) { + if (this.loop && this.onLoop) + { + if (this.animationSpeed > 0 && this.currentFrame < previousFrame) + { + this.onLoop(); + } + else if (this.animationSpeed < 0 && this.currentFrame > previousFrame) + { + this.onLoop(); + } + } + this.updateTexture(); } } diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js index 1340fcc..0592b54 100644 --- a/src/extras/BitmapText.js +++ b/src/extras/BitmapText.js @@ -96,16 +96,18 @@ * Disable by setting value to 0 * * @member {number} + * @private */ - this.maxWidth = 0; + this._maxWidth = 0; /** * The max line height. This is useful when trying to use the total height of the Text, * ie: when trying to vertically align. * * @member {number} + * @private */ - this.maxLineHeight = 0; + this._maxLineHeight = 0; /** * Text anchor. read-only @@ -144,6 +146,7 @@ let line = 0; let lastSpace = -1; let lastSpaceWidth = 0; + let spacesRemoved = 0; let maxLineHeight = 0; for (let i = 0; i < this.text.length; i++) @@ -168,11 +171,12 @@ continue; } - if (lastSpace !== -1 && this.maxWidth > 0 && pos.x * scale > this.maxWidth) + if (lastSpace !== -1 && this._maxWidth > 0 && pos.x * scale > this._maxWidth) { - core.utils.removeItems(chars, lastSpace, i - lastSpace); + core.utils.removeItems(chars, lastSpace - spacesRemoved, i - lastSpace); i = lastSpace; lastSpace = -1; + ++spacesRemoved; lineWidths.push(lastSpaceWidth); maxLineWidth = Math.max(maxLineWidth, lastSpaceWidth); @@ -275,7 +279,7 @@ this._glyphs[i].y -= this._textHeight * this.anchor.y; } } - this.maxLineHeight = maxLineHeight * scale; + this._maxLineHeight = maxLineHeight * scale; } /** @@ -430,6 +434,42 @@ } /** + * The max width of this bitmap text in pixels. If the text provided is longer than the + * value provided, line breaks will be automatically inserted in the last whitespace. + * Disable by setting value to 0 + * + * @member {number} + */ + get maxWidth() + { + return this._maxWidth; + } + + set maxWidth(value) // eslint-disable-line require-jsdoc + { + if (this._maxWidth === value) + { + return; + } + this._maxWidth = value; + this.dirty = true; + } + + /** + * The max line height. This is useful when trying to use the total height of the Text, + * ie: when trying to vertically align. + * + * @member {number} + * @readonly + */ + get maxLineHeight() + { + this.validate(); + + return this._maxLineHeight; + } + + /** * The width of the overall text, different from fontSize, * which is defined in the style object * diff --git a/src/extras/TilingSprite.js b/src/extras/TilingSprite.js index 72fee21..6282341 100644 --- a/src/extras/TilingSprite.js +++ b/src/extras/TilingSprite.js @@ -206,7 +206,8 @@ } else { - tempCanvas.context.drawImage(baseTexture.source, -texture._frame.x, -texture._frame.y); + tempCanvas.context.drawImage(baseTexture.source, + -texture._frame.x * baseTextureResolution, -texture._frame.y * baseTextureResolution); } this._canvasPattern = tempCanvas.context.createPattern(tempCanvas.canvas, 'repeat'); } @@ -310,11 +311,11 @@ const height = this._height; const x1 = -width * this.anchor._x; - if (tempPoint.x > x1 && tempPoint.x < x1 + width) + if (tempPoint.x >= x1 && tempPoint.x < x1 + width) { const y1 = -height * this.anchor._y; - if (tempPoint.y > y1 && tempPoint.y < y1 + height) + if (tempPoint.y >= y1 && tempPoint.y < y1 + height) { return true; } diff --git a/src/extras/webgl/TilingSpriteRenderer.js b/src/extras/webgl/TilingSpriteRenderer.js index 5734fc0..49c3a29 100644 --- a/src/extras/webgl/TilingSpriteRenderer.js +++ b/src/extras/webgl/TilingSpriteRenderer.js @@ -4,7 +4,6 @@ import { join } from 'path'; const tempMat = new core.Matrix(); -const tempArray = new Float32Array(4); /** * WebGL renderer plugin for tiling sprites @@ -141,17 +140,13 @@ } shader.uniforms.uTransform = tempMat.toArray(true); - - const color = tempArray; - - core.utils.hex2rgb(ts.tint, color); - color[3] = ts.worldAlpha; - shader.uniforms.uColor = color; + shader.uniforms.uColor = core.utils.premultiplyTintToRgba(ts.tint, ts.worldAlpha, + shader.uniforms.uColor, baseTex.premultipliedAlpha); shader.uniforms.translationMatrix = ts.transform.worldTransform.toArray(true); shader.uniforms.uSampler = renderer.bindTexture(tex); - renderer.setBlendMode(ts.blendMode); + renderer.setBlendMode(core.utils.correctBlendMode(ts.blendMode, baseTex.premultipliedAlpha)); quad.vao.draw(this.renderer.gl.TRIANGLES, 6, 0); } diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 9d59adb..d1f599f 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -104,7 +104,7 @@ glData.shader.uniforms.uSampler = renderer.bindTexture(texture); - renderer.state.setBlendMode(mesh.blendMode); + renderer.state.setBlendMode(core.utils.correctBlendMode(mesh.blendMode, texture.baseTexture.premultipliedAlpha)); if (glData.shader.uniforms.uTransform) { @@ -118,8 +118,9 @@ } } glData.shader.uniforms.translationMatrix = mesh.worldTransform.toArray(true); - glData.shader.uniforms.alpha = mesh.worldAlpha; - glData.shader.uniforms.tint = mesh.tintRgb; + + glData.shader.uniforms.uColor = core.utils.premultiplyRgba(mesh.tintRgb, + mesh.worldAlpha, glData.shader.uniforms.uColor, texture.baseTexture.premultipliedAlpha); const drawMode = mesh.drawMode === Mesh.DRAW_MODES.TRIANGLE_MESH ? gl.TRIANGLE_STRIP : gl.TRIANGLES; diff --git a/src/mesh/webgl/mesh.frag b/src/mesh/webgl/mesh.frag index 9e0b634..6096983 100644 --- a/src/mesh/webgl/mesh.frag +++ b/src/mesh/webgl/mesh.frag @@ -1,10 +1,9 @@ varying vec2 vTextureCoord; -uniform float alpha; -uniform vec3 tint; +uniform vec4 uColor; uniform sampler2D uSampler; void main(void) { - gl_FragColor = texture2D(uSampler, vTextureCoord) * vec4(tint * alpha, alpha); + gl_FragColor = texture2D(uSampler, vTextureCoord) * uColor; } diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js index 6d98098..d6dc59d 100644 --- a/src/particles/ParticleContainer.js +++ b/src/particles/ParticleContainer.js @@ -131,8 +131,8 @@ * @member {number} * @default 0xFFFFFF */ - this._tint = null; - this._tintRGB = []; + this._tint = 0; + this.tintRgb = new Float32Array(4); this.tint = 0xFFFFFF; } @@ -180,7 +180,7 @@ set tint(value) // eslint-disable-line require-jsdoc { this._tint = value; - hex2rgb(value, this._tintRGB); + hex2rgb(value, this.tintRgb); } /** diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js index 16e0c9c..aaf3638 100644 --- a/src/particles/webgl/ParticleRenderer.js +++ b/src/particles/webgl/ParticleRenderer.js @@ -142,8 +142,10 @@ buffers = container._glBuffers[renderer.CONTEXT_UID] = this.generateBuffers(container); } + const baseTexture = children[0]._texture.baseTexture; + // if the uvs have not updated then no point rendering just yet! - this.renderer.setBlendMode(container.blendMode); + this.renderer.setBlendMode(core.utils.correctBlendMode(container.blendMode, baseTexture.premultipliedAlpha)); const gl = renderer.gl; @@ -152,12 +154,11 @@ m.prepend(renderer._activeRenderTarget.projectionMatrix); this.shader.uniforms.projectionMatrix = m.toArray(true); - this.shader.uniforms.uAlpha = container.worldAlpha; - this.shader.uniforms.tint = container._tintRGB; + + this.shader.uniforms.uColor = core.utils.premultiplyRgba(container.tintRgb, + container.worldAlpha, this.shader.uniforms.uColor, baseTexture.premultipliedAlpha); // make sure the texture is bound.. - const baseTexture = children[0]._texture.baseTexture; - this.shader.uniforms.uSampler = renderer.bindTexture(baseTexture); // now lets upload and render the buffers.. diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js index ebd80d9..4361623 100644 --- a/src/particles/webgl/ParticleShader.js +++ b/src/particles/webgl/ParticleShader.js @@ -48,11 +48,10 @@ 'varying float vColor;', 'uniform sampler2D uSampler;', - 'uniform float uAlpha;', - 'uniform vec3 tint;', + 'uniform vec4 uColor;', 'void main(void){', - ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);', + ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uColor;', ' if (color.a == 0.0) discard;', ' gl_FragColor = color;', '}', diff --git a/test/core/BaseTexture.js b/test/core/BaseTexture.js index e99de40..703175d 100644 --- a/test/core/BaseTexture.js +++ b/test/core/BaseTexture.js @@ -102,11 +102,20 @@ expect(PIXI.utils.BaseTextureCache[NAME2]).to.equal(baseTexture); }); - it('destroying a destroyed BaseTexture should not throw an error', function () + it('should not throw an error destroying a destroyed BaseTexture', function () { const baseTexture = new PIXI.BaseTexture(); baseTexture.destroy(); baseTexture.destroy(); }); + + it('should set source.crossOrigin to anonymous if explicitly set', function () + { + cleanCache(); + + const baseTexture = PIXI.BaseTexture.fromImage(URL, true); + + expect(baseTexture.source.crossOrigin).to.equal('anonymous'); + }); }); diff --git a/test/core/CanvasRenderer.js b/test/core/CanvasRenderer.js new file mode 100644 index 0000000..7ebf633 --- /dev/null +++ b/test/core/CanvasRenderer.js @@ -0,0 +1,32 @@ +'use strict'; + +describe('PIXI.CanvasRenderer', function () +{ + it('should default context to rootContext', function () + { + const renderer = new PIXI.CanvasRenderer(1, 1); + + try + { + expect(renderer.context).to.equal(renderer.rootContext); + } + finally + { + renderer.destroy(); + } + }); + + it('should allow clear() to work despite no containers added to the renderer', function () + { + const renderer = new PIXI.CanvasRenderer(1, 1); + + try + { + renderer.clear(); + } + finally + { + renderer.destroy(); + } + }); +}); diff --git a/test/core/Rectangle.js b/test/core/Rectangle.js index 40ca3b0..edd5a99 100644 --- a/test/core/Rectangle.js +++ b/test/core/Rectangle.js @@ -135,7 +135,7 @@ it('should fit a rectangle', function () { - const rect1 = new PIXI.Rectangle(0, 0, -10, -10); + const rect1 = new PIXI.Rectangle(0, 0, 10, 10); const rect2 = new PIXI.Rectangle(-10, -10, 5, 5); rect2.fit(rect1); diff --git a/test/core/Sprite.js b/test/core/Sprite.js index b14419d..c9bc4cb 100755 --- a/test/core/Sprite.js +++ b/test/core/Sprite.js @@ -91,11 +91,26 @@ describe('containsPoint', function () { + const texture = new PIXI.RenderTexture.create(20, 30); + const sprite = new PIXI.Sprite(texture); + it('should return true when point inside', function () { const point = new PIXI.Point(10, 10); - const texture = new PIXI.RenderTexture.create(20, 30); - const sprite = new PIXI.Sprite(texture); + + expect(sprite.containsPoint(point)).to.be.true; + }); + + it('should return true when point on left edge', function () + { + const point = new PIXI.Point(0, 15); + + expect(sprite.containsPoint(point)).to.be.true; + }); + + it('should return true when point on top edge', function () + { + const point = new PIXI.Point(10, 0); expect(sprite.containsPoint(point)).to.be.true; }); @@ -103,8 +118,6 @@ it('should return false when point outside', function () { const point = new PIXI.Point(100, 100); - const texture = new PIXI.RenderTexture.create(20, 30); - const sprite = new PIXI.Sprite(texture); expect(sprite.containsPoint(point)).to.be.false; }); diff --git a/test/core/TilingSprite.js b/test/core/TilingSprite.js index 7694792..0b6c87f 100644 --- a/test/core/TilingSprite.js +++ b/test/core/TilingSprite.js @@ -30,7 +30,9 @@ const texture = new PIXI.Texture(new PIXI.BaseTexture()); const tilingSprite = new PIXI.extras.TilingSprite(texture, 200, 300); - expect(tilingSprite.containsPoint(new PIXI.Point(1, 1))).to.equal(true); + expect(tilingSprite.containsPoint(new PIXI.Point(0, 0))).to.equal(true); + expect(tilingSprite.containsPoint(new PIXI.Point(10, 10))).to.equal(true); + expect(tilingSprite.containsPoint(new PIXI.Point(200, 300))).to.equal(false); expect(tilingSprite.containsPoint(new PIXI.Point(300, 400))).to.equal(false); }); diff --git a/test/core/WebGLRenderer.js b/test/core/WebGLRenderer.js index 3d6cf98..4fe37a8 100644 --- a/test/core/WebGLRenderer.js +++ b/test/core/WebGLRenderer.js @@ -18,4 +18,18 @@ renderer.destroy(); } })); + + it('should allow clear() to work despite no containers added to the renderer', withGL(function () + { + const renderer = new PIXI.WebGLRenderer(1, 1); + + try + { + renderer.clear(); + } + finally + { + renderer.destroy(); + } + })); }); diff --git a/test/core/index.js b/test/core/index.js index 28636ab..2138e7b 100755 --- a/test/core/index.js +++ b/test/core/index.js @@ -25,6 +25,7 @@ require('./Circle'); require('./Graphics'); require('./SpriteRenderer'); +require('./CanvasRenderer'); require('./WebGLRenderer'); require('./Ellipse'); require('./BaseTexture');